usrloc: fix memory leak on DB_ONLY mode on RPC commands
[sip-router] / src / modules / usrloc / ul_rpc.c
1 /*
2  * usrloc module
3  *
4  * Copyright (C) 2009 Daniel-Constantin Mierla (asipto.com).
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18
19 #include "../../core/ip_addr.h"
20 #include "../../core/dprint.h"
21 #include "../../core/dset.h"
22 #include "../../lib/srutils/sruid.h"
23
24 #include "ul_rpc.h"
25 #include "dlist.h"
26 #include "ucontact.h"
27 #include "udomain.h"
28 #include "usrloc_mod.h"
29 #include "utime.h"
30
31 /*! CSEQ nr used */
32 #define RPC_UL_CSEQ 1
33 /*! call-id used for ul_add and ul_rm_contact */
34 static str rpc_ul_cid = str_init("dfjrewr12386fd6-343@kamailio.rpc");
35 /*! path used for ul_add and ul_rm_contact */
36 static str rpc_ul_path = str_init("dummypath");
37 /*! user agent used for ul_add */
38 static str rpc_ul_ua  = str_init(NAME " SIP Router - RPC Server");
39
40 extern sruid_t _ul_sruid;
41
42 static const char* ul_rpc_dump_doc[2] = {
43         "Dump user location tables",
44         0
45 };
46
47
48 int rpc_dump_contact(rpc_t* rpc, void* ctx, void *ih, ucontact_t* c)
49 {
50         void* vh;
51         str empty_str = {"[not set]", 9};
52         str state_str = {"[not set]", 9};
53         str socket_str = {"[not set]", 9};
54         time_t t;
55
56         t = time(0);
57         if(rpc->struct_add(ih, "{", "Contact", &vh)<0)
58         {
59                 rpc->fault(ctx, 500, "Internal error creating contact struct");
60                 return -1;
61         }
62         if(rpc->struct_add(vh, "S", "Address", &c->c)<0)
63         {
64                 rpc->fault(ctx, 500, "Internal error adding addr");
65                 return -1;
66         }
67         if (c->expires == 0) { if(rpc->struct_add(vh, "s", "Expires", "permanent")<0)
68                 {
69                         rpc->fault(ctx, 500, "Internal error adding expire");
70                         return -1;
71                 }
72         } else if (c->expires == UL_EXPIRED_TIME) {
73                 if(rpc->struct_add(vh, "s", "Expires", "deleted")<0)
74                 {
75                         rpc->fault(ctx, 500, "Internal error adding expire");
76                         return -1;
77                 }
78         } else if (t > c->expires) {
79                 if(rpc->struct_add(vh, "s", "Expires", "expired")<0)
80                 {
81                         rpc->fault(ctx, 500, "Internal error adding expire");
82                         return -1;
83                 }
84         } else {
85                 if(rpc->struct_add(vh, "d", "Expires", (int)(c->expires - t))<0)
86                 {
87                         rpc->fault(ctx, 500, "Internal error adding expire");
88                         return -1;
89                 }
90         }
91         if (c->state == CS_NEW) {
92                 state_str.s = "CS_NEW";
93                 state_str.len = 6;
94         } else if (c->state == CS_SYNC) {
95                 state_str.s = "CS_SYNC";
96                 state_str.len = 7;
97         } else if (c->state== CS_DIRTY) {
98                 state_str.s = "CS_DIRTY";
99                 state_str.len = 8;
100         } else {
101                 state_str.s = "CS_UNKNOWN";
102                 state_str.len = 10;
103         }
104         if(c->sock)
105         {
106                 socket_str.s = c->sock->sock_str.s;
107                 socket_str.len = c->sock->sock_str.len;
108         }
109         if(rpc->struct_add(vh, "f", "Q", q2double(c->q))<0)
110         {
111                 rpc->fault(ctx, 500, "Internal error adding q");
112                 return -1;
113         }
114         if(rpc->struct_add(vh, "S", "Call-ID", &c->callid)<0)
115         {
116                 rpc->fault(ctx, 500, "Internal error adding callid");
117                 return -1;
118         }
119         if(rpc->struct_add(vh, "d", "CSeq", c->cseq)<0)
120         {
121                 rpc->fault(ctx, 500, "Internal error adding cseq");
122                 return -1;
123         }
124         if(rpc->struct_add(vh, "S", "User-Agent",
125                                 (c->user_agent.len)?&c->user_agent: &empty_str)<0)
126         {
127                 rpc->fault(ctx, 500, "Internal error adding user-agent");
128                 return -1;
129         }
130         if(rpc->struct_add(vh, "S", "Received",
131                                 (c->received.len)?&c->received: &empty_str)<0)
132         {
133                 rpc->fault(ctx, 500, "Internal error adding received");
134                 return -1;
135         }
136         if(rpc->struct_add(vh, "S", "Path",
137                                 (c->path.len)?&c->path: &empty_str)<0)
138         {
139                 rpc->fault(ctx, 500, "Internal error adding path");
140                 return -1;
141         }
142         if(rpc->struct_add(vh, "S", "State", &state_str)<0)
143         {
144                 rpc->fault(ctx, 500, "Internal error adding state");
145                 return -1;
146         }
147         if(rpc->struct_add(vh, "d", "Flags", c->flags)<0)
148         {
149                 rpc->fault(ctx, 500, "Internal error adding flags");
150                 return -1;
151         }
152         if(rpc->struct_add(vh, "d", "CFlags", c->cflags)<0)
153         {
154                 rpc->fault(ctx, 500, "Internal error adding cflags");
155                 return -1;
156         }
157         if(rpc->struct_add(vh, "S", "Socket", &socket_str)<0)
158         {
159                 rpc->fault(ctx, 500, "Internal error adding socket");
160                 return -1;
161         }
162         if(rpc->struct_add(vh, "d", "Methods", c->methods)<0)
163         {
164                 rpc->fault(ctx, 500, "Internal error adding methods");
165                 return -1;
166         }
167         if(rpc->struct_add(vh, "S", "Ruid", (c->ruid.len)?&c->ruid: &empty_str)<0)
168         {
169                 rpc->fault(ctx, 500, "Internal error adding ruid");
170                 return -1;
171         }
172         if(rpc->struct_add(vh, "S", "Instance",
173                                 (c->instance.len)?&c->instance: &empty_str)<0)
174         {
175                 rpc->fault(ctx, 500, "Internal error adding instance");
176                 return -1;
177         }
178         if(rpc->struct_add(vh, "d", "Reg-Id", c->reg_id)<0)
179         {
180                 rpc->fault(ctx, 500, "Internal error adding reg_id");
181                 return -1;
182         }
183         if(rpc->struct_add(vh, "d", "Server-Id", c->server_id)<0)
184         {
185                 rpc->fault(ctx, 500, "Internal error adding server_id");
186                 return -1;
187         }
188         if(rpc->struct_add(vh, "d", "Tcpconn-Id", c->tcpconn_id)<0)
189         {
190                 rpc->fault(ctx, 500, "Internal error adding tcpconn_id");
191                 return -1;
192         }
193         if(rpc->struct_add(vh, "d", "Keepalive", c->keepalive)<0)
194         {
195                 rpc->fault(ctx, 500, "Internal error adding keepalive");
196                 return -1;
197         }
198         if(rpc->struct_add(vh, "d", "Last-Keepalive", (int)c->last_keepalive)<0)
199         {
200                 rpc->fault(ctx, 500, "Internal error adding last_keepalive");
201                 return -1;
202         }
203         if(rpc->struct_add(vh, "d", "Last-Modified", (int)c->last_modified)<0)
204         {
205                 rpc->fault(ctx, 500, "Internal error adding last_modified");
206                 return -1;
207         }
208         return 0;
209 }
210
211 static void ul_rpc_dump(rpc_t* rpc, void* ctx)
212 {
213         struct urecord* r;
214         dlist_t* dl;
215         udomain_t* dom;
216         str brief = {0, 0};
217         int summary = 0;
218         ucontact_t* c;
219         void* th;
220         void* dah;
221         void* dh;
222         void* ah;
223         void* bh;
224         void* ih;
225         void* sh;
226         int max, n, i;
227
228         rpc->scan(ctx, "*S", &brief);
229
230         if(brief.len==5 && (strncmp(brief.s, "brief", 5)==0))
231                 summary = 1;
232
233         if (rpc->add(ctx, "{", &th) < 0)
234         {
235                 rpc->fault(ctx, 500, "Internal error creating top rpc");
236                 return;
237         }
238         if (rpc->struct_add(th, "[", "Domains", &dah) < 0)
239         {
240                 rpc->fault(ctx, 500, "Internal error creating inner struct");
241                 return;
242         }
243         
244         for( dl=root ; dl ; dl=dl->next ) {
245                 dom = dl->d;
246
247                 if (rpc->struct_add(dah, "{", "Domain", &dh) < 0)
248                 {
249                         rpc->fault(ctx, 500, "Internal error creating inner struct");
250                         return;
251                 }
252
253                 if(rpc->struct_add(dh, "Sd[",
254                                         "Domain",  &dl->name,
255                                         "Size",    (int)dom->size,
256                                         "AoRs",    &ah)<0)
257                 {
258                         rpc->fault(ctx, 500, "Internal error creating inner struct");
259                         return;
260                 }
261                 for(i=0,n=0,max=0; i<dom->size; i++) {
262                         lock_ulslot( dom, i);
263                         n += dom->table[i].n;
264                         if(max<dom->table[i].n)
265                                 max= dom->table[i].n;
266                         for( r = dom->table[i].first ; r ; r=r->next ) {
267                                 if(summary==1)
268                                 {
269                                         if(rpc->struct_add(ah, "S",
270                                                                 "AoR", &r->aor)<0)
271                                         {
272                                                 unlock_ulslot( dom, i);
273                                                 rpc->fault(ctx, 500, "Internal error creating aor struct");
274                                                 return;
275                                         }
276                                 } else {
277                                         if(rpc->struct_add(ah, "{",
278                                                                 "Info", &bh)<0)
279                                         {
280                                                 unlock_ulslot( dom, i);
281                                                 rpc->fault(ctx, 500, "Internal error creating aor struct");
282                                                 return;
283                                         }
284                                         if(rpc->struct_add(bh, "Sd[",
285                                                                 "AoR", &r->aor,
286                                                                 "HashID", r->aorhash,
287                                                                 "Contacts", &ih)<0)
288                                         {
289                                                 unlock_ulslot( dom, i);
290                                                 rpc->fault(ctx, 500, "Internal error creating aor struct");
291                                                 return;
292                                         }
293                                         for( c=r->contacts ; c ; c=c->next)
294                                         {
295                                                 if (rpc_dump_contact(rpc, ctx, ih, c) == -1) {
296                                                         unlock_ulslot(dom, i);
297                                                         return;
298                                                 }
299                                         }
300                                 }
301                         }
302
303                         unlock_ulslot( dom, i);
304                 }
305
306                 /* extra attributes node */
307                 if(rpc->struct_add(dh, "{", "Stats",    &sh)<0)
308                 {
309                         rpc->fault(ctx, 500, "Internal error creating stats struct");
310                         return;
311                 }
312                 if(rpc->struct_add(sh, "dd",
313                                         "Records", n,
314                                         "Max-Slots", max)<0)
315                 {
316                         rpc->fault(ctx, 500, "Internal error adding stats");
317                         return;
318                 }
319         }
320 }
321
322 static const char* ul_rpc_lookup_doc[2] = {
323         "Lookup one AOR in the usrloc location table",
324         0
325 };
326
327 /*!
328  * \brief Search a domain in the global domain list
329  * \param table domain (table) name
330  * \return pointer to domain if found, 0 if not found
331  */
332 static inline udomain_t* rpc_find_domain(str* table)
333 {
334         dlist_t* dom;
335
336         for( dom=root ; dom ; dom=dom->next ) {
337                 if ((dom->name.len == table->len) &&
338                                 !memcmp(dom->name.s, table->s, table->len))
339                         return dom->d;
340         }
341         return 0;
342 }
343
344 /*!
345  * \brief Convert address of record
346  *
347  * Convert an address of record string to lower case, and truncate
348  * it when use_domain is not set.
349  * \param aor address of record
350  * \return 0 on success, -1 on error
351  */
352 static inline int rpc_fix_aor(str *aor)
353 {
354         char *p;
355
356         p = memchr( aor->s, '@', aor->len);
357         if (use_domain) {
358                 if (p==NULL)
359                         return -1;
360         } else {
361                 if (p)
362                         aor->len = p - aor->s;
363         }
364         if(!get_aor_case_sensitive())
365                 strlower(aor);
366
367         return 0;
368 }
369
370 /*!
371  * \brief Dumps the contacts of an AOR
372  * \param rpc   Handle to RPC structure
373  * \param ctx not used
374  * \note expects 2 arguments: the table name and the AOR
375  */
376 static void ul_rpc_lookup(rpc_t* rpc, void* ctx)
377 {
378         udomain_t* dom;
379         str table = {0, 0};
380         str aor = {0, 0};
381         void* th;
382         void* ih;
383         urecord_t *rec;
384         ucontact_t* con;
385         int ret;
386         int rpl_tree;
387
388         if (rpc->scan(ctx, "S", &table) != 1) {
389                 rpc->fault(ctx, 500, "Not enough parameters (table and AOR to lookup)");
390                 return;
391         }
392         if (rpc->scan(ctx, "S", &aor) != 1) {
393                 rpc->fault(ctx, 500, "Not enough parameters (table and AOR to lookup)");
394                 return;
395         }
396
397         /* look for table */
398         dom = rpc_find_domain( &table );
399         if (dom == NULL) {
400                 rpc->fault(ctx, 500, "Domain not found");
401                 return;
402         }
403
404         /* process the aor */
405         if ( rpc_fix_aor(&aor) != 0 ) {
406                 rpc->fault(ctx, 500, "Domain missing in AOR");
407                 return;
408         }
409
410         lock_udomain( dom, &aor);
411
412         ret = get_urecord( dom, &aor, &rec);
413         if (ret == 1) {
414                 unlock_udomain( dom, &aor);
415                 rpc->fault(ctx, 500, "AOR not found in location table");
416                 return;
417         }
418
419         get_act_time();
420         rpl_tree = 0;
421
422         if (rpc->add(ctx, "{", &th) < 0)
423         {
424                 release_urecord(rec);
425                 unlock_udomain(dom, &aor);
426                 rpc->fault(ctx, 500, "Internal error creating outer rpc");
427                 return;
428         }
429         if(rpc->struct_add(th, "S[",
430                                 "AoR", &aor,
431                                 "Contacts", &ih)<0)
432         {
433                 release_urecord(rec);
434                 unlock_udomain(dom, &aor);
435                 rpc->fault(ctx, 500, "Internal error creating aor struct");
436                 return;
437         }
438
439         /* We have contacts, list them */
440         for( con=rec->contacts ; con ; con=con->next) {
441                 if (VALID_CONTACT( con, act_time)) {
442                         rpl_tree++;
443                         if (rpc_dump_contact(rpc, ctx, ih, con) == -1) {
444                                 release_urecord(rec);
445                                 unlock_udomain(dom, &aor);
446                                 return;
447                         }
448                 }
449         }
450         release_urecord(rec);
451         unlock_udomain( dom, &aor);
452
453         if (rpl_tree==0) {
454                 rpc->fault(ctx, 500, "AOR has no contacts");
455                 return;
456         }
457         return;
458 }
459
460 static void ul_rpc_rm_aor(rpc_t* rpc, void* ctx)
461 {
462         udomain_t* dom;
463         str table = {0, 0};
464         str aor = {0, 0};
465
466         if (rpc->scan(ctx, "SS", &table, &aor) != 2) {
467                 rpc->fault(ctx, 500, "Not enough parameters (table and AOR to lookup)");
468                 return;
469         }
470
471         /* look for table */
472         dom = rpc_find_domain( &table );
473         if (dom == NULL) {
474                 rpc->fault(ctx, 500, "Domain not found");
475                 return;
476         }
477
478         /* process the aor */
479         if ( rpc_fix_aor(&aor) != 0 ) {
480                 rpc->fault(ctx, 500, "Domain missing in AOR");
481                 return;
482         }
483
484         lock_udomain( dom, &aor);
485         if (delete_urecord( dom, &aor, 0) < 0) {
486                 unlock_udomain( dom, &aor);
487                 rpc->fault(ctx, 500, "Failed to delete AOR");
488                 return;
489         }
490
491         unlock_udomain( dom, &aor);
492         return;
493 }
494
495 static const char* ul_rpc_rm_aor_doc[2] = {
496         "Delete a address of record including its contacts",
497         0
498 };
499
500 static void ul_rpc_rm_contact(rpc_t* rpc, void* ctx)
501 {
502         udomain_t* dom;
503         str table = {0, 0};
504         str aor = {0, 0};
505         str contact = {0, 0};
506         urecord_t *rec;
507         ucontact_t* con;
508         int ret;
509
510         if (rpc->scan(ctx, "SSS", &table, &aor, &contact) != 3) {
511                 rpc->fault(ctx, 500, "Not enough parameters (table, AOR and contact)");
512                 return;
513         }
514
515         /* look for table */
516         dom = rpc_find_domain( &table );
517         if (dom == NULL) {
518                 rpc->fault(ctx, 500, "Domain not found");
519                 return;
520         }
521
522         /* process the aor */
523         if ( rpc_fix_aor(&aor) != 0 ) {
524                 rpc->fault(ctx, 500, "Domain missing in AOR");
525                 return;
526         }
527
528         lock_udomain( dom, &aor);
529
530         ret = get_urecord( dom, &aor, &rec);
531         if (ret == 1) {
532                 unlock_udomain( dom, &aor);
533                 rpc->fault(ctx, 404, "AOR not found");
534                 return;
535         }
536
537         ret = get_ucontact( rec, &contact, &rpc_ul_cid, &rpc_ul_path, RPC_UL_CSEQ+1, &con);
538         if (ret < 0) {
539                 release_urecord(rec);
540                 unlock_udomain( dom, &aor);
541                 rpc->fault(ctx, 500, "Internal error (can't get contact)");
542                 return;
543         }
544         if (ret > 0) {
545                 release_urecord(rec);
546                 unlock_udomain( dom, &aor);
547                 rpc->fault(ctx, 404, "Contact not found");
548                 return;
549         }
550
551         if (delete_ucontact(rec, con) < 0) {
552                 release_urecord(rec);
553                 unlock_udomain( dom, &aor);
554                 rpc->fault(ctx, 500, "Internal error (can't delete contact)");
555                 return;
556         }
557
558         release_urecord(rec);
559         unlock_udomain( dom, &aor);
560         return;
561 }
562
563 static const char* ul_rpc_rm_contact_doc[2] = {
564         "Delete a contact from an AOR record",
565         0
566 };
567
568 static void ul_rpc_flush(rpc_t* rpc, void* ctx)
569 {
570         if(synchronize_all_udomains(0, 1) != 0) {
571                 rpc->fault(ctx, 500, "Operation failed");
572         }
573         return;
574 }
575
576 static const char* ul_rpc_flush_doc[2] = {
577         "Flush the usrloc memory cache to DB",
578         0
579 };
580
581 /**
582  * test if a rpc parameter is set
583  * - used for optional parameters which can be given '.' or '0' value
584  *   in order to indicate is not intended to be set
585  * - return: 1 if parameter is set; 0 if parameter is not set
586  */
587 int ul_rpc_is_param_set(str *p)
588 {
589         if(p==NULL || p->len==0 || p->s==NULL)
590                 return 0;
591         if(p->len>1)
592                 return 1;
593         if((strncmp(p->s, ".", 1)==0) || (strncmp(p->s, "0", 1)==0))
594                 return 0;
595         return 1;
596 }
597
598 /*!
599  * \brief Add a new contact for an address of record
600  * \note Expects 9 parameters: table name, AOR, contact, expires, Q,
601  * path, flags, cflags, methods
602  */
603 static void ul_rpc_add(rpc_t* rpc, void* ctx)
604 {
605         str table = {0, 0};
606         str aor = {0, 0};
607         str contact = {0, 0};
608         str path = {0, 0};
609         str received = {0, 0};
610         str socket = {0, 0};
611         str temp = {0, 0};
612         double dtemp;
613         ucontact_info_t ci;
614         urecord_t* r;
615         ucontact_t* c;
616         udomain_t *dom;
617         int ret;
618
619         memset(&ci, 0, sizeof(ucontact_info_t));
620
621         ret = rpc->scan(ctx, "SSSdfSddd*SS", &table, &aor, &contact, &ci.expires,
622                         &dtemp, &path, &ci.flags, &ci.cflags, &ci.methods, &received,
623                         &socket);
624         if (ret < 9) {
625                 LM_ERR("not enough parameters - read so far: %d\n", ret);
626                 rpc->fault(ctx, 500, "Not enough parameters or wrong format");
627                 return;
628         }
629         if(ul_rpc_is_param_set(&path)) {
630                 ci.path = &path;
631         } else {
632                 LM_DBG("path == 0 -> unset\n");
633         }
634         if(ret>9) {
635                 /* received parameter */
636                 if(ul_rpc_is_param_set(&received)) {
637                         ci.received.s = received.s;
638                         ci.received.len = received.len;
639                 }
640         }
641         if(ret>10) {
642                 /* socket parameter */
643                 if(ul_rpc_is_param_set(&socket)) {
644                         ci.sock = lookup_local_socket(&socket);
645                 }
646         }
647         LM_DBG("ret: %d table:%.*s aor:%.*s contact:%.*s expires:%d"
648                         " dtemp:%f path:%.*s flags:%d bflags:%d methods:%d\n",
649                         ret, table.len, table.s, aor.len, aor.s, contact.len, contact.s,
650                         (int) ci.expires, dtemp, (ci.path)?ci.path->len:0,
651                         (ci.path && ci.path->s)?ci.path->s:"", ci.flags, ci.cflags,
652                         (int) ci.methods);
653         ci.q = double2q(dtemp);
654         temp.s = q2str(ci.q, (unsigned int*)&temp.len);
655         LM_DBG("q:%.*s\n", temp.len, temp.s);
656         /* look for table */
657         dom = rpc_find_domain( &table );
658         if (dom == NULL) {
659                 rpc->fault(ctx, 500, "Domain not found");
660                 return;
661         }
662
663         /* process the aor */
664         if (rpc_fix_aor(&aor) != 0 ) {
665                 rpc->fault(ctx, 500, "Domain missing in AOR");
666                 return;
667         }
668
669         if(sruid_next_safe(&_ul_sruid)<0)
670         {
671                 rpc->fault(ctx, 500, "Can't obtain next uid");
672                 return;
673         }
674         ci.ruid = _ul_sruid.uid;
675         ci.server_id = server_id;
676
677         lock_udomain(dom, &aor);
678
679         ret = get_urecord(dom, &aor, &r);
680         if(ret==1) {
681                 if (insert_urecord(dom, &aor, &r) < 0)
682                 {
683                         unlock_udomain(dom, &aor);
684                         rpc->fault(ctx, 500, "Can't insert record");
685                         return;
686                 }
687                 c = 0;
688         } else {
689                 if (get_ucontact( r, &contact, &rpc_ul_cid, &rpc_ul_path,
690                                         RPC_UL_CSEQ+1, &c) < 0)
691                 {
692                         unlock_udomain(dom, &aor);
693                         rpc->fault(ctx, 500, "Can't get record");
694                         return;
695                 }
696         }
697
698         get_act_time();
699
700         ci.callid = &rpc_ul_cid;
701         ci.user_agent = &rpc_ul_ua;
702         ci.cseq = RPC_UL_CSEQ;
703         /* 0 expires means permanent contact */
704         if (ci.expires!=0)
705                 ci.expires += act_time;
706
707         if (c) {
708                 if (update_ucontact( r, c, &ci) < 0)
709                 {
710                         release_urecord(r);
711                         unlock_udomain(dom, &aor);
712                         rpc->fault(ctx, 500, "Can't update contact");
713                         return;
714                 }
715         } else {
716                 if ( insert_ucontact(r, &contact, &ci, &c) < 0 )
717                 {
718                         release_urecord(r);
719                         unlock_udomain(dom, &aor);
720                         rpc->fault(ctx, 500, "Can't insert contact");
721                         return;
722                 }
723         }
724
725         release_urecord(r);
726         unlock_udomain(dom, &aor);
727         return;
728 }
729
730 static const char* ul_rpc_add_doc[2] = {
731         "Add a new contact for an address of record",
732         0
733 };
734
735 #define QUERY_LEN 256
736
737 static void ul_rpc_db_users(rpc_t* rpc, void* ctx)
738 {
739         str table = {0, 0};
740         char query[QUERY_LEN];
741         str query_str;
742         db1_res_t* res = NULL;
743         int count = 0;
744
745         if (db_mode == NO_DB) {
746                 rpc->fault(ctx, 500, "Command is not supported in db_mode=0");
747                 return;
748         }
749
750         if (rpc->scan(ctx, "S", &table) != 1) {
751                 rpc->fault(ctx, 500, "Not enough parameters (table to lookup)");
752                 return;
753         }
754
755         if (user_col.len + domain_col.len + table.len + 32 > QUERY_LEN) {
756                 rpc->fault(ctx, 500, "Too long database query");
757                 return;
758         }
759
760         if (!DB_CAPABILITY(ul_dbf, DB_CAP_RAW_QUERY)) {
761                 rpc->fault(ctx, 500, "Database does not support raw queries");
762                 return;
763         }
764         if (ul_dbf.use_table(ul_dbh, &table) < 0) {
765                 rpc->fault(ctx, 500, "Failed to use table");
766                 return;
767         }
768
769         memset(query, 0, QUERY_LEN);
770         query_str.len = snprintf(query, QUERY_LEN,
771                         "SELECT COUNT(DISTINCT %.*s, %.*s) FROM %.*s WHERE (UNIX_TIMESTAMP(expires) = 0) OR (expires > NOW())",
772                         user_col.len, user_col.s,
773                         domain_col.len, domain_col.s,
774                         table.len, table.s);
775         query_str.s = query;
776         if (ul_dbf.raw_query(ul_dbh, &query_str, &res) < 0 || res==NULL) {
777                 rpc->fault(ctx, 500, "Failed to query AoR count");
778                 return;
779         }
780         if (RES_ROW_N(res) > 0) {
781                 count = (int)VAL_INT(ROW_VALUES(RES_ROWS(res)));
782         }
783         ul_dbf.free_result(ul_dbh, res);
784
785         rpc->add(ctx, "d", count);
786 }
787
788 static const char* ul_rpc_db_users_doc[2] = {
789         "Tell number of different unexpired users (AoRs) in database table (db_mode!=0 only)",
790         0
791 };
792
793 static void ul_rpc_db_contacts(rpc_t* rpc, void* ctx)
794 {
795         str table = {0, 0};
796         char query[QUERY_LEN];
797         str query_str;
798         db1_res_t* res = NULL;
799         int count = 0;
800
801         if (db_mode == NO_DB) {
802                 rpc->fault(ctx, 500, "Command is not supported in db_mode=0");
803                 return;
804         }
805
806         if (rpc->scan(ctx, "S", &table) != 1) {
807                 rpc->fault(ctx, 500, "Not enough parameters (table to lookup)");
808                 return;
809         }
810
811         if (table.len + 22 > QUERY_LEN) {
812                 rpc->fault(ctx, 500, "Too long database query");
813                 return;
814         }
815
816         if (!DB_CAPABILITY(ul_dbf, DB_CAP_RAW_QUERY)) {
817                 rpc->fault(ctx, 500, "Database does not support raw queries");
818                 return;
819         }
820         if (ul_dbf.use_table(ul_dbh, &table) < 0) {
821                 rpc->fault(ctx, 500, "Failed to use table");
822                 return;
823         }
824
825         memset(query, 0, QUERY_LEN);
826         query_str.len = snprintf(query, QUERY_LEN, "SELECT COUNT(*) FROM %.*s WHERE (UNIX_TIMESTAMP(expires) = 0) OR (expires > NOW())",
827                         table.len, table.s);
828         query_str.s = query;
829         if (ul_dbf.raw_query(ul_dbh, &query_str, &res) < 0 || res==NULL) {
830                 rpc->fault(ctx, 500, "Failed to query contact count");
831                 return;
832         }
833
834         if (RES_ROW_N(res) > 0) {
835                 count = (int)VAL_INT(ROW_VALUES(RES_ROWS(res)));
836         }
837         ul_dbf.free_result(ul_dbh, res);
838
839         rpc->add(ctx, "d", count);
840 }
841
842 static const char* ul_rpc_db_contacts_doc[2] = {
843         "Tell number of unexpired contacts in database table (db_mode=3 only)",
844         0
845 };
846
847 static void ul_rpc_db_expired_contacts(rpc_t* rpc, void* ctx)
848 {
849         str table = {0, 0};
850         char query[QUERY_LEN];
851         str query_str;
852         db1_res_t* res = NULL;
853         int count = 0;
854
855         if (db_mode == NO_DB) {
856                 rpc->fault(ctx, 500, "Command is not supported in db_mode=0");
857                 return;
858         }
859
860         if (rpc->scan(ctx, "S", &table) != 1) {
861                 rpc->fault(ctx, 500, "Not enough parameters (table to lookup)");
862                 return;
863         }
864
865         if (table.len + 22 > QUERY_LEN) {
866                 rpc->fault(ctx, 500, "Too long database query");
867                 return;
868         }
869
870         if (!DB_CAPABILITY(ul_dbf, DB_CAP_RAW_QUERY)) {
871                 rpc->fault(ctx, 500, "Database does not support raw queries");
872                 return;
873         }
874         if (ul_dbf.use_table(ul_dbh, &table) < 0) {
875                 rpc->fault(ctx, 500, "Failed to use table");
876                 return;
877         }
878
879         memset(query, 0, QUERY_LEN);
880         query_str.len = snprintf(query, QUERY_LEN, "SELECT COUNT(*) FROM %.*s WHERE (UNIX_TIMESTAMP(expires) > 0) AND (expires <= NOW())",
881                         table.len, table.s);
882         query_str.s = query;
883         if (ul_dbf.raw_query(ul_dbh, &query_str, &res) < 0 || res==NULL) {
884                 rpc->fault(ctx, 500, "Failed to query contact count");
885                 return;
886         }
887
888         if (RES_ROW_N(res) > 0) {
889                 count = (int)VAL_INT(ROW_VALUES(RES_ROWS(res)));
890         }
891         ul_dbf.free_result(ul_dbh, res);
892
893         rpc->add(ctx, "d", count);
894 }
895
896 static const char* ul_rpc_db_expired_contacts_doc[2] = {
897         "Tell number of expired contacts in database table (db_mode=3 only)",
898         0
899 };
900
901 rpc_export_t ul_rpc[] = {
902         {"ul.dump",   ul_rpc_dump,   ul_rpc_dump_doc,   0},
903         {"ul.lookup",   ul_rpc_lookup,   ul_rpc_lookup_doc,   0},
904         {"ul.rm", ul_rpc_rm_aor, ul_rpc_rm_aor_doc, 0},
905         {"ul.rm_contact", ul_rpc_rm_contact, ul_rpc_rm_contact_doc, 0},
906         {"ul.flush", ul_rpc_flush, ul_rpc_flush_doc, 0},
907         {"ul.add", ul_rpc_add, ul_rpc_add_doc, 0},
908         {"ul.db_users", ul_rpc_db_users, ul_rpc_db_users_doc, 0},
909         {"ul.db_contacts", ul_rpc_db_contacts, ul_rpc_db_contacts_doc, 0},
910         {"ul.db_expired_contacts", ul_rpc_db_expired_contacts, ul_rpc_db_expired_contacts_doc, 0},
911         {0, 0, 0, 0}
912 };
913
914