/*
 * $Id: ul_rpc.c,v 1.9 2007/05/02 09:28:21 liborc Exp $
 *
 * 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 "../../dprint.h"
#include "../../ut.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)
{
        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) < 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) < 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_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;
        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);
		      
        rpc_find_domain(&table, &d);
        if (d) {
                lock_udomain(d);

                if (add_contact(d, &uid, &contact, expires, qval, flags) < 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, "Sfd",
                                        "contact", &_c->c,
                                        "q", q2double(_c->q),
                                        "expires", (int)(_c->expires - act_time));
                }
		
                _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.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}
};
