usrloc(k): better safety check for null record access
[sip-router] / modules_k / usrloc / udomain.c
1 /*
2  * $Id$ 
3  *
4  * Copyright (C) 2001-2003 FhG Fokus
5  *
6  * This file is part of Kamailio, a free SIP server.
7  *
8  * Kamailio is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version
12  *
13  * Kamailio is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License 
19  * along with this program; if not, write to the Free Software 
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  * History:
23  * ---------
24  * 2003-03-11 changed to the new locking scheme: locking.h (andrei)
25  * 2003-03-12 added replication mark and zombie state (nils)
26  * 2004-06-07 updated to the new DB api (andrei)
27  * 2004-08-23  hash function changed to process characters as unsigned
28  *             -> no negative results occur (jku)
29  */
30
31 /*! \file
32  *  \brief USRLOC - Userloc domain handling functions
33  *  \ingroup usrloc
34  *
35  * - Module: \ref usrloc
36  */
37
38 #include "udomain.h"
39 #include <string.h>
40 #include "../../parser/parse_methods.h"
41 #include "../../mem/shm_mem.h"
42 #include "../../dprint.h"
43 #include "../../lib/srdb1/db.h"
44 #include "../../socket_info.h"
45 #include "../../ut.h"
46 #include "../../hashes.h"
47 #include "ul_mod.h"            /* usrloc module parameters */
48 #include "usrloc.h"
49 #include "utime.h"
50 #include "usrloc.h"
51
52 #ifdef STATISTICS
53 static char *build_stat_name( str* domain, char *var_name)
54 {
55         int n;
56         char *s;
57         char *p;
58
59         n = domain->len + 1 + strlen(var_name) + 1;
60         s = (char*)shm_malloc( n );
61         if (s==0) {
62                 LM_ERR("no more shm mem\n");
63                 return 0;
64         }
65         memcpy( s, domain->s, domain->len);
66         p = s + domain->len;
67         *(p++) = '-';
68         memcpy( p , var_name, strlen(var_name));
69         p += strlen(var_name);
70         *(p++) = 0;
71         return s;
72 }
73 #endif
74
75
76 /*!
77  * \brief Create a new domain structure
78  * \param  _n is pointer to str representing name of the domain, the string is
79  * not copied, it should point to str structure stored in domain list
80  * \param _s is hash table size
81  * \param _d new created domain
82  * \return 0 on success, -1 on failure
83  */
84 int new_udomain(str* _n, int _s, udomain_t** _d)
85 {
86         int i;
87 #ifdef STATISTICS
88         char *name;
89 #endif
90         
91         /* Must be always in shared memory, since
92          * the cache is accessed from timer which
93          * lives in a separate process
94          */
95         *_d = (udomain_t*)shm_malloc(sizeof(udomain_t));
96         if (!(*_d)) {
97                 LM_ERR("new_udomain(): No memory left\n");
98                 goto error0;
99         }
100         memset(*_d, 0, sizeof(udomain_t));
101         
102         (*_d)->table = (hslot_t*)shm_malloc(sizeof(hslot_t) * _s);
103         if (!(*_d)->table) {
104                 LM_ERR("no memory left 2\n");
105                 goto error1;
106         }
107
108         (*_d)->name = _n;
109         
110         for(i = 0; i < _s; i++) {
111                 init_slot(*_d, &((*_d)->table[i]), i);
112         }
113
114         (*_d)->size = _s;
115
116 #ifdef STATISTICS
117         /* register the statistics */
118         if ( (name=build_stat_name(_n,"users"))==0 || register_stat("usrloc",
119         name, &(*_d)->users, STAT_NO_RESET|STAT_SHM_NAME)!=0 ) {
120                 LM_ERR("failed to add stat variable\n");
121                 goto error2;
122         }
123         if ( (name=build_stat_name(_n,"contacts"))==0 || register_stat("usrloc",
124         name, &(*_d)->contacts, STAT_NO_RESET|STAT_SHM_NAME)!=0 ) {
125                 LM_ERR("failed to add stat variable\n");
126                 goto error2;
127         }
128         if ( (name=build_stat_name(_n,"expires"))==0 || register_stat("usrloc",
129         name, &(*_d)->expires, STAT_SHM_NAME)!=0 ) {
130                 LM_ERR("failed to add stat variable\n");
131                 goto error2;
132         }
133 #endif
134
135         return 0;
136 #ifdef STATISTICS
137 error2:
138         shm_free((*_d)->table);
139 #endif
140 error1:
141         shm_free(*_d);
142 error0:
143         return -1;
144 }
145
146
147 /*!
148  * \brief Free all memory allocated for the domain
149  * \param _d freed domain
150  */
151 void free_udomain(udomain_t* _d)
152 {
153         int i;
154         
155         if (_d->table) {
156                 for(i = 0; i < _d->size; i++) {
157                         lock_ulslot(_d, i);
158                         deinit_slot(_d->table + i);
159                         unlock_ulslot(_d, i);
160                 }
161                 shm_free(_d->table);
162         }
163         shm_free(_d);
164 }
165
166
167 /*!
168  * \brief Returns a static dummy urecord for temporary usage
169  * \param _d domain (needed for the name)
170  * \param _aor address of record
171  * \param _r new created urecord
172  */
173 static inline void get_static_urecord(udomain_t* _d, str* _aor,
174                                                                                                                 struct urecord** _r)
175 {
176         static struct urecord r;
177
178         memset( &r, 0, sizeof(struct urecord) );
179         r.aor = *_aor;
180         r.aorhash = ul_get_aorhash(_aor);
181         r.domain = _d->name;
182         *_r = &r;
183 }
184
185
186 /*!
187  * \brief Debugging helper function
188  */
189 void print_udomain(FILE* _f, udomain_t* _d)
190 {
191         int i;
192         int max=0, slot=0, n=0;
193         struct urecord* r;
194         fprintf(_f, "---Domain---\n");
195         fprintf(_f, "name : '%.*s'\n", _d->name->len, ZSW(_d->name->s));
196         fprintf(_f, "size : %d\n", _d->size);
197         fprintf(_f, "table: %p\n", _d->table);
198         /*fprintf(_f, "lock : %d\n", _d->lock); -- can be a structure --andrei*/
199         fprintf(_f, "\n");
200         for(i=0; i<_d->size; i++)
201         {
202                 r = _d->table[i].first;
203                 n += _d->table[i].n;
204                 if(max<_d->table[i].n){
205                         max= _d->table[i].n;
206                         slot = i;
207                 }
208                 while(r) {
209                         print_urecord(_f, r);
210                         r = r->next;
211                 }
212         }
213         fprintf(_f, "\nMax slot: %d (%d/%d)\n", max, slot, n);
214         fprintf(_f, "\n---/Domain---\n");
215 }
216
217
218 /*!
219  * \brief Convert database values into ucontact_info
220  *
221  * Convert database values into ucontact_info, 
222  * expects 12 rows (contact, expirs, q, callid, cseq, flags,
223  * ua, received, path, socket, methods, last_modified)
224  * \param vals database values
225  * \param contact contact
226  * \return pointer to the ucontact_info on success, 0 on failure
227  */
228 static inline ucontact_info_t* dbrow2info( db_val_t *vals, str *contact)
229 {
230         static ucontact_info_t ci;
231         static str callid, ua, received, host, path;
232         int port, proto;
233         char *p;
234
235         memset( &ci, 0, sizeof(ucontact_info_t));
236
237         contact->s = (char*)VAL_STRING(vals);
238         if (VAL_NULL(vals) || contact->s==0 || contact->s[0]==0) {
239                 LM_CRIT("bad contact\n");
240                 return 0;
241         }
242         contact->len = strlen(contact->s);
243
244         if (VAL_NULL(vals+1)) {
245                 LM_CRIT("empty expire\n");
246                 return 0;
247         }
248         ci.expires = VAL_TIME(vals+1);
249
250         if (VAL_NULL(vals+2)) {
251                 LM_CRIT("empty q\n");
252                 return 0;
253         }
254         ci.q = double2q(VAL_DOUBLE(vals+2));
255
256         if (VAL_NULL(vals+4)) {
257                 LM_CRIT("empty cseq_nr\n");
258                 return 0;
259         }
260         ci.cseq = VAL_INT(vals+4);
261
262         callid.s = (char*)VAL_STRING(vals+3);
263         if (VAL_NULL(vals+3) || !callid.s || !callid.s[0]) {
264                 LM_CRIT("bad callid\n");
265                 return 0;
266         }
267         callid.len  = strlen(callid.s);
268         ci.callid = &callid;
269
270         if (VAL_NULL(vals+5)) {
271                 LM_CRIT("empty flag\n");
272                 return 0;
273         }
274         ci.flags  = VAL_BITMAP(vals+5);
275
276         if (VAL_NULL(vals+6)) {
277                 LM_CRIT("empty cflag\n");
278                 return 0;
279         }
280         ci.cflags  = VAL_BITMAP(vals+6);
281
282         ua.s  = (char*)VAL_STRING(vals+7);
283         if (VAL_NULL(vals+7) || !ua.s || !ua.s[0]) {
284                 ua.s = 0;
285                 ua.len = 0;
286         } else {
287                 ua.len = strlen(ua.s);
288         }
289         ci.user_agent = &ua;
290
291         received.s  = (char*)VAL_STRING(vals+8);
292         if (VAL_NULL(vals+8) || !received.s || !received.s[0]) {
293                 received.len = 0;
294                 received.s = 0;
295         } else {
296                 received.len = strlen(received.s);
297         }
298         ci.received = received;
299         
300         path.s  = (char*)VAL_STRING(vals+9);
301                 if (VAL_NULL(vals+9) || !path.s || !path.s[0]) {
302                         path.len = 0;
303                         path.s = 0;
304                 } else {
305                         path.len = strlen(path.s);
306                 }
307         ci.path= &path;
308
309         /* socket name */
310         p  = (char*)VAL_STRING(vals+10);
311         if (VAL_NULL(vals+10) || p==0 || p[0]==0){
312                 ci.sock = 0;
313         } else {
314                 if (parse_phostport( p, &host.s, &host.len, 
315                 &port, &proto)!=0) {
316                         LM_ERR("bad socket <%s>\n", p);
317                         return 0;
318                 }
319                 ci.sock = grep_sock_info( &host, (unsigned short)port, proto);
320                 if (ci.sock==0) {
321                         LM_WARN("non-local socket <%s>...ignoring\n", p);
322                 }
323         }
324
325         /* supported methods */
326         if (VAL_NULL(vals+11)) {
327                 ci.methods = ALL_METHODS;
328         } else {
329                 ci.methods = VAL_BITMAP(vals+11);
330         }
331
332         /* last modified time */
333         if (!VAL_NULL(vals+12)) {
334                 ci.last_modified = VAL_TIME(vals+12);
335         }
336
337         /* record internal uid */
338         if (!VAL_NULL(vals+13)) {
339                 ci.ruid.s = (char*)VAL_STRING(vals+13);
340                 ci.ruid.len = strlen(ci.ruid.s);
341         }
342
343         /* sip instance */
344         if (!VAL_NULL(vals+14)) {
345                 ci.instance.s = (char*)VAL_STRING(vals+14);
346                 ci.instance.len = strlen(ci.instance.s);
347         }
348
349         /* reg-id */
350         if (!VAL_NULL(vals+15)) {
351                 ci.reg_id = VAL_UINT(vals+15);
352         }
353
354         return &ci;
355 }
356
357
358 /*!
359  * \brief Load all records from a udomain
360  *
361  * Load all records from a udomain, useful to populate the
362  * memory cache on startup.
363  * \param _c database connection
364  * \param _d loaded domain
365  * \return 0 on success, -1 on failure
366  */
367 int preload_udomain(db1_con_t* _c, udomain_t* _d)
368 {
369         char uri[MAX_URI_SIZE];
370         ucontact_info_t *ci;
371         db_row_t *row;
372         db_key_t columns[18];
373         db1_res_t* res = NULL;
374         str user, contact;
375         char* domain;
376         int i;
377         int n;
378
379         urecord_t* r;
380         ucontact_t* c;
381
382         columns[0] = &user_col;
383         columns[1] = &contact_col;
384         columns[2] = &expires_col;
385         columns[3] = &q_col;
386         columns[4] = &callid_col;
387         columns[5] = &cseq_col;
388         columns[6] = &flags_col;
389         columns[7] = &cflags_col;
390         columns[8] = &user_agent_col;
391         columns[9] = &received_col;
392         columns[10] = &path_col;
393         columns[11] = &sock_col;
394         columns[12] = &methods_col;
395         columns[13] = &last_mod_col;
396         columns[14] = &ruid_col;
397         columns[15] = &instance_col;
398         columns[16] = &reg_id_col;
399         columns[17] = &domain_col;
400
401         if (ul_dbf.use_table(_c, _d->name) < 0) {
402                 LM_ERR("sql use_table failed\n");
403                 return -1;
404         }
405
406 #ifdef EXTRA_DEBUG
407         LM_NOTICE("load start time [%d]\n", (int)time(NULL));
408 #endif
409
410         if (DB_CAPABILITY(ul_dbf, DB_CAP_FETCH)) {
411                 if (ul_dbf.query(_c, 0, 0, 0, columns, 0, (use_domain)?(18):(17), 0,
412                 0) < 0) {
413                         LM_ERR("db_query (1) failed\n");
414                         return -1;
415                 }
416                 if(ul_dbf.fetch_result(_c, &res, ul_fetch_rows)<0) {
417                         LM_ERR("fetching rows failed\n");
418                         return -1;
419                 }
420         } else {
421                 if (ul_dbf.query(_c, 0, 0, 0, columns, 0, (use_domain)?(18):(17), 0,
422                 &res) < 0) {
423                         LM_ERR("db_query failed\n");
424                         return -1;
425                 }
426         }
427
428         if (RES_ROW_N(res) == 0) {
429                 LM_DBG("table is empty\n");
430                 ul_dbf.free_result(_c, res);
431                 return 0;
432         }
433
434
435         n = 0;
436         do {
437                 LM_DBG("loading records - cycle [%d]\n", ++n);
438                 for(i = 0; i < RES_ROW_N(res); i++) {
439                         row = RES_ROWS(res) + i;
440
441                         user.s = (char*)VAL_STRING(ROW_VALUES(row));
442                         if (VAL_NULL(ROW_VALUES(row)) || user.s==0 || user.s[0]==0) {
443                                 LM_CRIT("empty username record in table %s...skipping\n",
444                                                 _d->name->s);
445                                 continue;
446                         }
447                         user.len = strlen(user.s);
448
449                         ci = dbrow2info( ROW_VALUES(row)+1, &contact);
450                         if (ci==0) {
451                                 LM_ERR("sipping record for %.*s in table %s\n",
452                                                 user.len, user.s, _d->name->s);
453                                 continue;
454                         }
455
456                         if (use_domain) {
457                                 domain = (char*)VAL_STRING(ROW_VALUES(row) + 17);
458                                 if (VAL_NULL(ROW_VALUES(row)+17) || domain==0 || domain[0]==0){
459                                         LM_CRIT("empty domain record for user %.*s...skipping\n",
460                                                         user.len, user.s);
461                                         continue;
462                                 }
463                                 /* user.s cannot be NULL - checked previosly */
464                                 user.len = snprintf(uri, MAX_URI_SIZE, "%.*s@%s",
465                                         user.len, user.s, domain);
466                                 user.s = uri;
467                                 if (user.s[user.len]!=0) {
468                                         LM_CRIT("URI '%.*s@%s' longer than %d\n", user.len, user.s,
469                                                         domain, MAX_URI_SIZE);
470                                         continue;
471                                 }
472                         }
473
474                 
475                         lock_udomain(_d, &user);
476                         if (get_urecord(_d, &user, &r) > 0) {
477                                 if (mem_insert_urecord(_d, &user, &r) < 0) {
478                                         LM_ERR("failed to create a record\n");
479                                         unlock_udomain(_d, &user);
480                                         goto error;
481                                 }
482                         }
483
484                         if ( (c=mem_insert_ucontact(r, &contact, ci)) == 0) {
485                                 LM_ERR("inserting contact failed\n");
486                                 unlock_udomain(_d, &user);
487                                 goto error1;
488                         }
489
490                         /* We have to do this, because insert_ucontact sets state to CS_NEW
491                          * and we have the contact in the database already */
492                         c->state = CS_SYNC;
493                         unlock_udomain(_d, &user);
494                 }
495
496                 if (DB_CAPABILITY(ul_dbf, DB_CAP_FETCH)) {
497                         if(ul_dbf.fetch_result(_c, &res, ul_fetch_rows)<0) {
498                                 LM_ERR("fetching rows (1) failed\n");
499                                 ul_dbf.free_result(_c, res);
500                                 return -1;
501                         }
502                 } else {
503                         break;
504                 }
505         } while(RES_ROW_N(res)>0);
506
507         ul_dbf.free_result(_c, res);
508
509 #ifdef EXTRA_DEBUG
510         LM_NOTICE("load end time [%d]\n", (int)time(NULL));
511 #endif
512
513         return 0;
514 error1:
515         free_ucontact(c);
516 error:
517         ul_dbf.free_result(_c, res);
518         return -1;
519 }
520
521
522 /*!
523  * \brief Loads from DB all contacts for an AOR
524  * \param _c database connection
525  * \param _d domain
526  * \param _aor address of record
527  * \return pointer to the record on success, 0 on errors or if nothing is found
528  */
529 urecord_t* db_load_urecord(db1_con_t* _c, udomain_t* _d, str *_aor)
530 {
531         ucontact_info_t *ci;
532         db_key_t columns[16];
533         db_key_t keys[2];
534         db_key_t order;
535         db_val_t vals[2];
536         db1_res_t* res = NULL;
537         str contact;
538         char *domain;
539         int i;
540
541         urecord_t* r;
542         ucontact_t* c;
543
544         keys[0] = &user_col;
545         vals[0].type = DB1_STR;
546         vals[0].nul = 0;
547         if (use_domain) {
548                 keys[1] = &domain_col;
549                 vals[1].type = DB1_STR;
550                 vals[1].nul = 0;
551                 domain = memchr(_aor->s, '@', _aor->len);
552                 vals[0].val.str_val.s   = _aor->s;
553                 if (domain==0) {
554                         vals[0].val.str_val.len = 0;
555                         vals[1].val.str_val = *_aor;
556                 } else {
557                         vals[0].val.str_val.len = domain - _aor->s;
558                         vals[1].val.str_val.s   = domain+1;
559                         vals[1].val.str_val.len = _aor->s + _aor->len - domain - 1;
560                 }
561         } else {
562                 vals[0].val.str_val = *_aor;
563         }
564
565         columns[0] = &contact_col;
566         columns[1] = &expires_col;
567         columns[2] = &q_col;
568         columns[3] = &callid_col;
569         columns[4] = &cseq_col;
570         columns[5] = &flags_col;
571         columns[6] = &cflags_col;
572         columns[7] = &user_agent_col;
573         columns[8] = &received_col;
574         columns[9] = &path_col;
575         columns[10] = &sock_col;
576         columns[11] = &methods_col;
577         columns[12] = &last_mod_col;
578         columns[13] = &ruid_col;
579         columns[14] = &instance_col;
580         columns[15] = &reg_id_col;
581
582         if (desc_time_order)
583                 order = &last_mod_col;
584         else
585                 order = &q_col;
586
587         if (ul_dbf.use_table(_c, _d->name) < 0) {
588                 LM_ERR("failed to use table %.*s\n", _d->name->len, _d->name->s);
589                 return 0;
590         }
591
592         if (ul_dbf.query(_c, keys, 0, vals, columns, (use_domain)?2:1, 16, order,
593                                 &res) < 0) {
594                 LM_ERR("db_query failed\n");
595                 return 0;
596         }
597
598         if (RES_ROW_N(res) == 0) {
599                 LM_DBG("aor %.*s not found in table %.*s\n",_aor->len, _aor->s, _d->name->len, _d->name->s);
600                 ul_dbf.free_result(_c, res);
601                 return 0;
602         }
603
604         r = 0;
605
606         for(i = 0; i < RES_ROW_N(res); i++) {
607                 ci = dbrow2info(  ROW_VALUES(RES_ROWS(res) + i), &contact);
608                 if (ci==0) {
609                         LM_ERR("skipping record for %.*s in table %s\n",
610                                         _aor->len, _aor->s, _d->name->s);
611                         continue;
612                 }
613                 
614                 if ( r==0 )
615                         get_static_urecord( _d, _aor, &r);
616
617                 if ( (c=mem_insert_ucontact(r, &contact, ci)) == 0) {
618                         LM_ERR("mem_insert failed\n");
619                         free_urecord(r);
620                         ul_dbf.free_result(_c, res);
621                         return 0;
622                 }
623
624                 /* We have to do this, because insert_ucontact sets state to CS_NEW
625                  * and we have the contact in the database already */
626                 c->state = CS_SYNC;
627         }
628
629         ul_dbf.free_result(_c, res);
630         return r;
631 }
632
633 /*!
634  * \brief Loads from DB all contacts for a RUID
635  * \param _c database connection
636  * \param _d domain
637  * \param _aor address of record
638  * \return pointer to the record on success, 0 on errors or if nothing is found
639  */
640 urecord_t* db_load_urecord_by_ruid(db1_con_t* _c, udomain_t* _d, str *_ruid)
641 {
642         ucontact_info_t *ci;
643         db_key_t columns[18];
644         db_key_t keys[1];
645         db_key_t order;
646         db_val_t vals[1];
647         db1_res_t* res = NULL;
648         db_row_t *row;
649         str contact;
650         str aor;
651         char aorbuf[512];
652         str domain;
653
654         urecord_t* r;
655         ucontact_t* c;
656
657         keys[0] = &ruid_col;
658         vals[0].type = DB1_STR;
659         vals[0].nul = 0;
660         vals[0].val.str_val = *_ruid;
661
662         columns[0] = &contact_col;
663         columns[1] = &expires_col;
664         columns[2] = &q_col;
665         columns[3] = &callid_col;
666         columns[4] = &cseq_col;
667         columns[5] = &flags_col;
668         columns[6] = &cflags_col;
669         columns[7] = &user_agent_col;
670         columns[8] = &received_col;
671         columns[9] = &path_col;
672         columns[10] = &sock_col;
673         columns[11] = &methods_col;
674         columns[12] = &last_mod_col;
675         columns[13] = &ruid_col;
676         columns[14] = &instance_col;
677         columns[15] = &reg_id_col;
678         columns[16] = &user_col;
679         columns[17] = &domain_col;
680
681         if (desc_time_order)
682                 order = &last_mod_col;
683         else
684                 order = &q_col;
685
686         if (ul_dbf.use_table(_c, _d->name) < 0) {
687                 LM_ERR("failed to use table %.*s\n", _d->name->len, _d->name->s);
688                 return 0;
689         }
690
691         if (ul_dbf.query(_c, keys, 0, vals, columns, 1, 18, order,
692                                 &res) < 0) {
693                 LM_ERR("db_query failed\n");
694                 return 0;
695         }
696
697         if (RES_ROW_N(res) == 0) {
698                 LM_DBG("aor %.*s not found in table %.*s\n",_ruid->len, _ruid->s,
699                                 _d->name->len, _d->name->s);
700                 ul_dbf.free_result(_c, res);
701                 return 0;
702         }
703
704         r = 0;
705
706         /* use first row - shouldn't be more */
707         row = RES_ROWS(res);
708
709         ci = dbrow2info(ROW_VALUES(RES_ROWS(res)), &contact);
710         if (ci==0) {
711                 LM_ERR("skipping record for %.*s in table %s\n",
712                                 _ruid->len, _ruid->s, _d->name->s);
713                 goto done;
714         }
715
716         aor.s = (char*)VAL_STRING(ROW_VALUES(row) + 16);
717         aor.len = strlen(aor.s);
718
719         if (use_domain) {
720                 domain.s = (char*)VAL_STRING(ROW_VALUES(row) + 17);
721                 if (VAL_NULL(ROW_VALUES(row)+17) || domain.s==0 || domain.s[0]==0){
722                         LM_CRIT("empty domain record for user %.*s...skipping\n",
723                                         aor.len, aor.s);
724                         goto done;
725                 }
726                 domain.len = strlen(domain.s);
727                 if(aor.len + domain.len + 2 >= 512) {
728                         LM_ERR("AoR is too big\n");
729                         goto done;
730                 }
731                 memcpy(aorbuf, aor.s, aor.len);
732                 aorbuf[aor.len] = '@';
733                 memcpy(aorbuf + aor.len + 1, domain.s, domain.len);
734                 aor.len += 1 + domain.len;
735                 aor.s = aorbuf;
736                 aor.s[aor.len] = '\0';
737         }
738         get_static_urecord( _d, &aor, &r);
739
740         if ( (c=mem_insert_ucontact(r, &contact, ci)) == 0) {
741                 LM_ERR("mem_insert failed\n");
742                 free_urecord(r);
743                 ul_dbf.free_result(_c, res);
744                 return 0;
745         }
746
747         /* We have to do this, because insert_ucontact sets state to CS_NEW
748          * and we have the contact in the database already */
749         c->state = CS_SYNC;
750
751 done:
752         ul_dbf.free_result(_c, res);
753         return r;
754 }
755
756
757 /*!
758  * \brief Timer function to cleanup expired contacts, DB_ONLY db_mode
759  * \param _d cleaned domain
760  * \return 0 on success, -1 on failure
761  */
762 int db_timer_udomain(udomain_t* _d)
763 {
764         db_key_t keys[2];
765         db_op_t  ops[2];
766         db_val_t vals[2];
767
768         keys[0] = &expires_col;
769         ops[0] = "<";
770         vals[0].type = DB1_DATETIME;
771         vals[0].nul = 0;
772         vals[0].val.time_val = act_time + 1;
773
774         keys[1] = &expires_col;
775         ops[1] = "!=";
776         vals[1].type = DB1_DATETIME;
777         vals[1].nul = 0;
778         vals[1].val.time_val = 0;
779
780         if (ul_dbf.use_table(ul_dbh, _d->name) < 0) {
781                 LM_ERR("use_table failed\n");
782                 return -1;
783         }
784
785         if (ul_dbf.delete(ul_dbh, keys, ops, vals, 2) < 0) {
786                 LM_ERR("failed to delete from table %s\n",_d->name->s);
787                 return -1;
788         }
789
790         return 0;
791 }
792
793
794 /*!
795  * \brief performs a dummy query just to see if DB is ok
796  * \param con database connection
797  * \param d domain
798  */
799 int testdb_udomain(db1_con_t* con, udomain_t* d)
800 {
801         db_key_t key[1], col[1];
802         db_val_t val[1];
803         db1_res_t* res = NULL;
804
805         if (ul_dbf.use_table(con, d->name) < 0) {
806                 LM_ERR("failed to change table\n");
807                 return -1;
808         }
809
810         key[0] = &user_col;
811
812         col[0] = &user_col;
813         VAL_TYPE(val) = DB1_STRING;
814         VAL_NULL(val) = 0;
815         VAL_STRING(val) = "dummy_user";
816         
817         if (ul_dbf.query( con, key, 0, val, col, 1, 1, 0, &res) < 0) {
818                 LM_ERR("failure in db_query\n");
819                 return -1;
820         }
821
822         ul_dbf.free_result( con, res);
823         return 0;
824 }
825
826
827 /*!
828  * \brief Insert a new record into domain in memory
829  * \param _d domain the record belongs to
830  * \param _aor address of record
831  * \param _r new created record
832  * \return 0 on success, -1 on failure
833  */
834 int mem_insert_urecord(udomain_t* _d, str* _aor, struct urecord** _r)
835 {
836         int sl;
837         
838         if (new_urecord(_d->name, _aor, _r) < 0) {
839                 LM_ERR("creating urecord failed\n");
840                 return -1;
841         }
842
843         sl = ((*_r)->aorhash)&(_d->size-1);
844         slot_add(&_d->table[sl], *_r);
845         update_stat( _d->users, 1);
846         return 0;
847 }
848
849
850 /*!
851  * \brief Remove a record from domain in memory
852  * \param _d domain the record belongs to
853  * \param _r deleted record
854  */
855 void mem_delete_urecord(udomain_t* _d, struct urecord* _r)
856 {
857         slot_rem(_r->slot, _r);
858         free_urecord(_r);
859         update_stat( _d->users, -1);
860 }
861
862
863 /*!
864  * \brief Run timer handler for given domain
865  * \param _d domain
866  */
867 void mem_timer_udomain(udomain_t* _d, int istart, int istep)
868 {
869         struct urecord* ptr, *t;
870         int i;
871
872         for(i=istart; i<_d->size; i+=istep)
873         {
874                 lock_ulslot(_d, i);
875
876                 ptr = _d->table[i].first;
877
878                 while(ptr) {
879                         timer_urecord(ptr);
880                         /* Remove the entire record if it is empty */
881                         if (ptr->contacts == 0) {
882                                 t = ptr;
883                                 ptr = ptr->next;
884                                 mem_delete_urecord(_d, t);
885                         } else {
886                                 ptr = ptr->next;
887                         }
888                 }
889                 unlock_ulslot(_d, i);
890         }
891 }
892
893
894 /*!
895  * \brief Get lock for a domain
896  * \param _d domain
897  * \param _aor adress of record, used as hash source for the lock slot
898  */
899 void lock_udomain(udomain_t* _d, str* _aor)
900 {
901         unsigned int sl;
902         if (db_mode!=DB_ONLY)
903         {
904                 sl = ul_get_aorhash(_aor) & (_d->size - 1);
905
906 #ifdef GEN_LOCK_T_PREFERED
907                 lock_get(_d->table[sl].lock);
908 #else
909                 ul_lock_idx(_d->table[sl].lockidx);
910 #endif
911         }
912 }
913
914
915 /*!
916  * \brief Release lock for a domain
917  * \param _d domain
918  * \param _aor address of record, uses as hash source for the lock slot
919  */
920 void unlock_udomain(udomain_t* _d, str* _aor)
921 {
922         unsigned int sl;
923         if (db_mode!=DB_ONLY)
924         {
925                 sl = ul_get_aorhash(_aor) & (_d->size - 1);
926 #ifdef GEN_LOCK_T_PREFERED
927                 lock_release(_d->table[sl].lock);
928 #else
929                 ul_release_idx(_d->table[sl].lockidx);
930 #endif
931         }
932 }
933
934 /*!
935  * \brief  Get lock for a slot
936  * \param _d domain
937  * \param i slot number
938  */
939 void lock_ulslot(udomain_t* _d, int i)
940 {
941         if (db_mode!=DB_ONLY)
942 #ifdef GEN_LOCK_T_PREFERED
943                 lock_get(_d->table[i].lock);
944 #else
945                 ul_lock_idx(_d->table[i].lockidx);
946 #endif
947 }
948
949
950 /*!
951  * \brief Release lock for a slot
952  * \param _d domain
953  * \param i slot number
954  */
955 void unlock_ulslot(udomain_t* _d, int i)
956 {
957         if (db_mode!=DB_ONLY)
958 #ifdef GEN_LOCK_T_PREFERED
959                 lock_release(_d->table[i].lock);
960 #else
961                 ul_release_idx(_d->table[i].lockidx);
962 #endif
963 }
964
965
966
967 /*!
968  * \brief Create and insert a new record
969  * \param _d domain to insert the new record
970  * \param _aor address of the record
971  * \param _r new created record
972  * \return return 0 on success, -1 on failure
973  */
974 int insert_urecord(udomain_t* _d, str* _aor, struct urecord** _r)
975 {
976         if (db_mode!=DB_ONLY) {
977                 if (mem_insert_urecord(_d, _aor, _r) < 0) {
978                         LM_ERR("inserting record failed\n");
979                         return -1;
980                 }
981         } else {
982                 get_static_urecord( _d, _aor, _r);
983         }
984         return 0;
985 }
986
987
988 /*!
989  * \brief Obtain a urecord pointer if the urecord exists in domain
990  * \param _d domain to search the record
991  * \param _aor address of record
992  * \param _r new created record
993  * \return 0 if a record was found, 1 if nothing could be found
994  */
995 int get_urecord(udomain_t* _d, str* _aor, struct urecord** _r)
996 {
997         unsigned int sl, i, aorhash;
998         urecord_t* r;
999
1000         if (db_mode!=DB_ONLY) {
1001                 /* search in cache */
1002                 aorhash = ul_get_aorhash(_aor);
1003                 sl = aorhash&(_d->size-1);
1004                 r = _d->table[sl].first;
1005
1006                 for(i = 0; r!=NULL && i < _d->table[sl].n; i++) {
1007                         if((r->aorhash==aorhash) && (r->aor.len==_aor->len)
1008                                                 && !memcmp(r->aor.s,_aor->s,_aor->len)){
1009                                 *_r = r;
1010                                 return 0;
1011                         }
1012
1013                         r = r->next;
1014                 }
1015         } else {
1016                 /* search in DB */
1017                 r = db_load_urecord( ul_dbh, _d, _aor);
1018                 if (r) {
1019                         *_r = r;
1020                         return 0;
1021                 }
1022         }
1023
1024         return 1;   /* Nothing found */
1025 }
1026
1027 /*!
1028  * \brief Obtain a urecord pointer if the urecord exists in domain (lock slot)
1029  * \param _d domain to search the record
1030  * \param _aorhash hash id for address of record
1031  * \param _ruid record internal unique id
1032  * \param _r store pointer to location record
1033  * \param _c store pointer to contact structure
1034  * \return 0 if a record was found, 1 if nothing could be found
1035  */
1036 int get_urecord_by_ruid(udomain_t* _d, unsigned int _aorhash,
1037                 str *_ruid, struct urecord** _r, struct ucontact** _c)
1038 {
1039         unsigned int sl, i;
1040         urecord_t* r;
1041         ucontact_t* c;
1042
1043         sl = _aorhash&(_d->size-1);
1044         lock_ulslot(_d, sl);
1045
1046         if (db_mode!=DB_ONLY) {
1047                 /* search in cache */
1048                 r = _d->table[sl].first;
1049
1050                 for(i = 0; i < _d->table[sl].n; i++) {
1051                         if(r->aorhash==_aorhash) {
1052                                 c = r->contacts;
1053                                 while(c) {
1054                                         if(c->ruid.len==_ruid->len
1055                                                         && !memcmp(c->ruid.s, _ruid->s, _ruid->len)) {
1056                                                 *_r = r;
1057                                                 *_c = c;
1058                                                 return 0;
1059                                         }
1060                                 }
1061                         }
1062                         r = r->next;
1063                 }
1064         } else {
1065                 /* search in DB */
1066                 r = db_load_urecord_by_ruid(ul_dbh, _d, _ruid);
1067                 if (r) {
1068                         if(r->aorhash==_aorhash) {
1069                                 c = r->contacts;
1070                                 while(c) {
1071                                         if(c->ruid.len==_ruid->len
1072                                                         && !memcmp(c->ruid.s, _ruid->s, _ruid->len)) {
1073                                                 *_r = r;
1074                                                 *_c = c;
1075                                                 return 0;
1076                                         }
1077                                 }
1078                         }
1079                 }
1080         }
1081
1082         unlock_ulslot(_d, (_aorhash & (_d->size - 1)));
1083         return -1;   /* Nothing found */
1084 }
1085
1086 /*!
1087  * \brief Delete a urecord from domain
1088  * \param _d domain where the record should be deleted
1089  * \param _aor address of record
1090  * \param _r deleted record
1091  * \return 0 on success, -1 if the record could not be deleted
1092  */
1093 int delete_urecord(udomain_t* _d, str* _aor, struct urecord* _r)
1094 {
1095         struct ucontact* c, *t;
1096
1097         if (db_mode==DB_ONLY) {
1098                 if (_r==0)
1099                         get_static_urecord( _d, _aor, &_r);
1100                 if (db_delete_urecord(_r)<0) {
1101                         LM_ERR("DB delete failed\n");
1102                         return -1;
1103                 }
1104                 free_urecord(_r);
1105                 return 0;
1106         }
1107
1108         if (_r==0) {
1109                 if (get_urecord(_d, _aor, &_r) > 0) {
1110                         return 0;
1111                 }
1112         }
1113
1114         c = _r->contacts;
1115         while(c) {
1116                 t = c;
1117                 c = c->next;
1118                 if (delete_ucontact(_r, t) < 0) {
1119                         LM_ERR("deleting contact failed\n");
1120                         return -1;
1121                 }
1122         }
1123         release_urecord(_r);
1124         return 0;
1125 }