More 32->64 bit codec conversions.
[asterisk/asterisk.git] / channels / chan_skinny.c
index 9051caf..79eb2fb 100644 (file)
@@ -49,11 +49,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/pbx.h"
 #include "asterisk/sched.h"
 #include "asterisk/io.h"
-#include "asterisk/rtp.h"
+#include "asterisk/rtp_engine.h"
 #include "asterisk/netsock.h"
 #include "asterisk/acl.h"
 #include "asterisk/callerid.h"
 #include "asterisk/cli.h"
+#include "asterisk/manager.h"
 #include "asterisk/say.h"
 #include "asterisk/cdr.h"
 #include "asterisk/astdb.h"
@@ -70,6 +71,63 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/indications.h"
 #include "asterisk/linkedlists.h"
 
+/*** DOCUMENTATION
+       <manager name="SKINNYdevices" language="en_US">
+               <synopsis>
+                       List SKINNY devices (text format).
+               </synopsis>
+               <syntax>
+                       <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
+               </syntax>
+               <description>
+                       <para>Lists Skinny devices in text format with details on current status.
+                       Devicelist will follow as separate events, followed by a final event called
+                       DevicelistComplete.</para>
+               </description>
+       </manager>
+       <manager name="SKINNYshowdevice" language="en_US">
+               <synopsis>
+                       Show SKINNY device (text format).
+               </synopsis>
+               <syntax>
+                       <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
+                       <parameter name="Device" required="true">
+                               <para>The device name you want to check.</para>
+                       </parameter>
+               </syntax>
+               <description>
+                       <para>Show one SKINNY device with details on current status.</para>
+               </description>
+       </manager>
+       <manager name="SKINNYlines" language="en_US">
+               <synopsis>
+                       List SKINNY lines (text format).
+               </synopsis>
+               <syntax>
+                       <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
+               </syntax>
+               <description>
+                       <para>Lists Skinny lines in text format with details on current status.
+                       Linelist will follow as separate events, followed by a final event called
+                       LinelistComplete.</para>
+               </description>
+       </manager>
+       <manager name="SKINNYshowline" language="en_US">
+               <synopsis>
+                       Show SKINNY line (text format).
+               </synopsis>
+               <syntax>
+                       <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
+                       <parameter name="Line" required="true">
+                               <para>The line name you want to check.</para>
+                       </parameter>
+               </syntax>
+               <description>
+                       <para>Show one SKINNY line with details on current status.</para>
+               </description>
+       </manager>
+ ***/
+
 #ifdef SKINNY_DEVMODE
 #define SKINNY_DEVONLY(code)   \
        code
@@ -83,7 +141,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 static const char tdesc[] = "Skinny Client Control Protocol (Skinny)";
 static const char config[] = "skinny.conf";
 
-static int default_capability = AST_FORMAT_ULAW | AST_FORMAT_ALAW;
+static format_t default_capability = AST_FORMAT_ULAW | AST_FORMAT_ALAW;
 static struct ast_codec_pref default_prefs;
 
 enum skinny_codecs {
@@ -584,7 +642,7 @@ struct soft_key_template_definition {
 #define SOFTKEY_DND 0x13
 #define SOFTKEY_IDIVERT 0x14
 
-struct soft_key_template_definition soft_key_template_default[] = {
+static struct soft_key_template_definition soft_key_template_default[] = {
        { "\200\001", SOFTKEY_REDIAL },
        { "\200\002", SOFTKEY_NEWCALL },
        { "\200\003", SOFTKEY_HOLD },
@@ -962,21 +1020,22 @@ struct skinny_req {
 /* XXX This is the combined size of the variables above.  (len, res, e)
    If more are added, this MUST change.
    (sizeof(skinny_req) - sizeof(skinny_data)) DOES NOT WORK on all systems (amd64?). */
-int skinny_header_size = 12;
+static int skinny_header_size = 12;
 
 /*****************************
  * Asterisk specific globals *
  *****************************/
 
 static int skinnydebug = 0;
+static int skinnyreload = 0;
 
 /* a hostname, portnumber, socket and such is usefull for VoIP protocols */
 static struct sockaddr_in bindaddr;
 static char ourhost[256];
 static int ourport;
 static struct in_addr __ourip;
-struct ast_hostent ahp;
-struct hostent *hp;
+static struct ast_hostent ahp;
+static struct hostent *hp;
 static int skinnysock = -1;
 static pthread_t accept_t;
 static int callnums = 1;
@@ -1074,7 +1133,7 @@ static int callnums = 1;
 #define SKINNY_CX_INACTIVE 4
 
 #if 0
-static char *skinny_cxmodes[] = {
+static const char * const skinny_cxmodes[] = {
        "sendonly",
        "recvonly",
        "sendrecv",
@@ -1109,8 +1168,8 @@ static int matchdigittimeout = 3000;
 struct skinny_subchannel {
        ast_mutex_t lock;
        struct ast_channel *owner;
-       struct ast_rtp *rtp;
-       struct ast_rtp *vrtp;
+       struct ast_rtp_instance *rtp;
+       struct ast_rtp_instance *vrtp;
        unsigned int callid;
        /* time_t lastouttime; */ /* Unused */
        int progress;
@@ -1170,9 +1229,9 @@ struct skinny_subchannel {
        int instance;                                   \
        int group;                                      \
        int needdestroy;                                \
-       int confcapability;                             \
+       format_t confcapability;                                \
        struct ast_codec_pref confprefs;                \
-       int capability;                                 \
+       format_t capability;                                    \
        struct ast_codec_pref prefs;                    \
        int nonCodecCapability;                         \
        int onhooktime;                                 \
@@ -1180,7 +1239,8 @@ struct skinny_subchannel {
        int immediate;                                  \
        int hookstate;                                  \
        int nat;                                        \
-       int canreinvite;
+       int directmedia;                                \
+       int prune;
 
 struct skinny_line {
        SKINNY_LINE_OPTIONS
@@ -1195,7 +1255,7 @@ struct skinny_line {
        int newmsgs;
 };
 
-struct skinny_line_options{
+static struct skinny_line_options{
        SKINNY_LINE_OPTIONS
 } default_line_struct = {
        .callwaiting = 1,
@@ -1205,15 +1265,16 @@ struct skinny_line_options{
        .hidecallerid = 0,
        .amaflags = 0,
        .instance = 0,
-       .canreinvite = 0,
+       .directmedia = 0,
        .nat = 0,
        .confcapability = AST_FORMAT_ULAW | AST_FORMAT_ALAW,
        .capability = 0,
        .getforward = 0,
        .needdestroy = 0,
+       .prune = 0,
        .hookstate = SKINNY_ONHOOK,
 };
-struct skinny_line_options *default_line = &default_line_struct;
+static struct skinny_line_options *default_line = &default_line_struct;
 
 static AST_LIST_HEAD_STATIC(lines, skinny_line);
 
@@ -1248,14 +1309,15 @@ struct skinny_addon {
        int registered;                                         \
        int lastlineinstance;                                   \
        int lastcallreference;                                  \
-       int confcapability;                                     \
+       format_t confcapability;                                        \
        struct ast_codec_pref confprefs;                        \
-       int capability;                                         \
+       format_t capability;                                            \
        int earlyrtp;                                           \
        int transfer;                                           \
        int callwaiting;                                        \
        int mwiblink;                                           \
-       int dnd;
+       int dnd;                                                \
+       int prune;
 
 struct skinny_device {
        SKINNY_DEVICE_OPTIONS
@@ -1273,7 +1335,7 @@ struct skinny_device {
        AST_LIST_ENTRY(skinny_device) list;
 };
 
-struct skinny_device_options{
+static struct skinny_device_options {
        SKINNY_DEVICE_OPTIONS
 } default_device_struct = {
        .transfer = 1,
@@ -1283,8 +1345,9 @@ struct skinny_device_options{
        .dnd = 0,
        .confcapability = AST_FORMAT_ULAW | AST_FORMAT_ALAW,
        .capability = 0,
+       .prune = 0,
 };
-struct skinny_device_options *default_device = &default_device_struct;
+static struct skinny_device_options *default_device = &default_device_struct;
        
 static AST_LIST_HEAD_STATIC(devices, skinny_device);
 
@@ -1308,9 +1371,9 @@ struct skinnysession {
        AST_LIST_ENTRY(skinnysession) list;
 };
 
+static struct ast_channel *skinny_request(const char *type, format_t format, const struct ast_channel *requestor, void *data, int *cause);
 static AST_LIST_HEAD_STATIC(sessions, skinnysession);
 
-static struct ast_channel *skinny_request(const char *type, int format, void *data, int *cause);
 static int skinny_devicestate(void *data);
 static int skinny_call(struct ast_channel *ast, char *dest, int timeout);
 static int skinny_hangup(struct ast_channel *ast);
@@ -1323,6 +1386,7 @@ static int skinny_senddigit_begin(struct ast_channel *ast, char digit);
 static int skinny_senddigit_end(struct ast_channel *ast, char digit, unsigned int duration);
 static int handle_time_date_req_message(struct skinny_req *req, struct skinnysession *s);
 static void mwi_event_cb(const struct ast_event *event, void *userdata);
+static int skinny_reload(void);
 
 static const struct ast_channel_tech skinny_tech = {
        .type = "Skinny",
@@ -1340,7 +1404,7 @@ static const struct ast_channel_tech skinny_tech = {
        .fixup = skinny_fixup,
        .send_digit_begin = skinny_senddigit_begin,
        .send_digit_end = skinny_senddigit_end,
-       .bridge = ast_rtp_bridge,  
+       .bridge = ast_rtp_instance_bridge, 
 };
 
 static int skinny_extensionstate_cb(char *context, char* exten, int state, void *data);
@@ -1651,7 +1715,7 @@ static struct skinny_speeddial *find_speeddial_by_instance(struct skinny_device
        return sd;
 }
 
-static int codec_skinny2ast(enum skinny_codecs skinnycodec)
+static format_t codec_skinny2ast(enum skinny_codecs skinnycodec)
 {
        switch (skinnycodec) {
        case SKINNY_CODEC_ALAW:
@@ -1673,7 +1737,7 @@ static int codec_skinny2ast(enum skinny_codecs skinnycodec)
        }
 }
 
-static int codec_ast2skinny(int astcodec)
+static int codec_ast2skinny(format_t astcodec)
 {
        switch (astcodec) {
        case AST_FORMAT_ALAW:
@@ -1840,6 +1904,7 @@ static int skinny_register(struct skinny_req *req, struct skinnysession *s)
                        AST_LIST_TRAVERSE(&d->lines, l, list) {
                                /* FIXME: All sorts of issues will occur if this line is already connected to a device */
                                if (l->device) {
+                                       manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: Skinny\r\nPeer: Skinny/%s@%s\r\nPeerStatus: Rejected\r\nCause: LINE_ALREADY_CONNECTED\r\n", l->name, l->device->name); 
                                        ast_verb(1, "Line %s already connected to %s. Not connecting to %s.\n", l->name, l->device->name, d->name);
                                } else {
                                        l->device = d;
@@ -1853,6 +1918,7 @@ static int skinny_register(struct skinny_req *req, struct skinnysession *s)
                                        l->instance = instance;
                                        l->newmsgs = ast_app_has_voicemail(l->mailbox, NULL);
                                        set_callforwards(l, NULL, 0);
+                                       manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: Skinny\r\nPeer: Skinny/%s@%s\r\nPeerStatus: Registered\r\n", l->name, d->name);
                                        register_exten(l);
                                        /* initialize MWI on line and device */
                                        mwi_event_cb(0, l);
@@ -1892,6 +1958,7 @@ static int skinny_unregister(struct skinny_req *req, struct skinnysession *s)
                                l->capability = 0;
                                ast_parse_allow_disallow(&l->prefs, &l->capability, "all", 0);                  
                                l->instance = 0;
+                               manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: Skinny\r\nPeer: Skinny/%s@%s\r\nPeerStatus: Unregistered\r\n", l->name, d->name);
                                unregister_exten(l);
                                ast_devstate_changed(AST_DEVICE_UNAVAILABLE, "Skinny/%s@%s", l->name, d->name);
                        }
@@ -2243,6 +2310,9 @@ static void transmit_displaymessage(struct skinny_device *d, const char *text, i
                //req->data.clearpromptstatus.lineInstance = instance;
                //req->data.clearpromptstatus.callReference = reference;
 
+               /* send datetime message. We have to do it here because it will clear the display on the phone if we do it elsewhere */
+               handle_time_date_req_message(NULL, d->session);
+
                if (skinnydebug)
                        ast_verb(1, "Clearing Display\n");
        } else {
@@ -2506,91 +2576,126 @@ static int skinny_extensionstate_cb(char *context, char *exten, int state, void
        return 0;
 }
 
-static void mwi_event_cb(const struct ast_event *event, void *userdata)
+static void update_connectedline(struct skinny_subchannel *sub, const void *data, size_t datalen)
 {
-       struct skinny_line *l = userdata;
+       struct ast_channel *c = sub->owner;
+       struct skinny_line *l = sub->parent;
        struct skinny_device *d = l->device;
-       struct skinnysession *s = d->session;
-       struct skinny_line *l2;
-       int new_msgs = 0;
-       int dev_msgs = 0;
 
-       if (s) {
-               if (event) {
-                       l->newmsgs = ast_event_get_ie_uint(event, AST_EVENT_IE_NEWMSGS);
-               }
+       if (ast_strlen_zero(c->cid.cid_num) || ast_strlen_zero(c->connected.id.number))
+               return;
 
-               if (l->newmsgs) {
-                       transmit_lamp_indication(d, STIMULUS_VOICEMAIL, l->instance, l->mwiblink?SKINNY_LAMP_BLINK:SKINNY_LAMP_ON);
+       if (sub->owner->_state == AST_STATE_UP) {
+               transmit_callstate(d, l->instance, SKINNY_CONNECTED, sub->callid);
+               transmit_displaypromptstatus(d, "Connected", 0, l->instance, sub->callid);
+               if (sub->outgoing)
+                       transmit_callinfo(d, c->connected.id.name, c->connected.id.number, l->cid_name, l->cid_num, l->instance, sub->callid, 1);
+               else
+                       transmit_callinfo(d, l->cid_name, l->cid_num, c->connected.id.name, c->connected.id.number, l->instance, sub->callid, 2);
+       } else {
+               if (sub->outgoing) {
+                       transmit_callstate(d, l->instance, SKINNY_RINGIN, sub->callid);
+                       transmit_displaypromptstatus(d, "Ring-In", 0, l->instance, sub->callid);
+                       transmit_callinfo(d, c->connected.id.name, c->connected.id.number, l->cid_name, l->cid_num, l->instance, sub->callid, 1);
                } else {
-                       transmit_lamp_indication(d, STIMULUS_VOICEMAIL, l->instance, SKINNY_LAMP_OFF);
-               }
-
-               /* find out wether the device lamp should be on or off */
-               AST_LIST_TRAVERSE(&d->lines, l2, list) {
-                       if (l2->newmsgs) {
-                               dev_msgs++;
+                       if (!sub->ringing) {
+                               transmit_callstate(d, l->instance, SKINNY_RINGOUT, sub->callid);
+                               transmit_displaypromptstatus(d, "Ring-Out", 0, l->instance, sub->callid);
+                               sub->ringing = 1;
+                       } else {
+                               transmit_callstate(d, l->instance, SKINNY_PROGRESS, sub->callid);
+                               transmit_displaypromptstatus(d, "Call Progress", 0, l->instance, sub->callid);
+                               sub->progress = 1;
                        }
-               }
 
-               if (dev_msgs) {
-                       transmit_lamp_indication(d, STIMULUS_VOICEMAIL, 0, d->mwiblink?SKINNY_LAMP_BLINK:SKINNY_LAMP_ON);
-               } else {
-                       transmit_lamp_indication(d, STIMULUS_VOICEMAIL, 0, SKINNY_LAMP_OFF);
+                       transmit_callinfo(d, l->cid_name, l->cid_num, c->connected.id.name, c->connected.id.number, l->instance, sub->callid, 2);
                }
-               ast_verb(3, "Skinny mwi_event_cb found %d new messages\n", new_msgs);
        }
 }
 
-static void do_housekeeping(struct skinnysession *s)
+static void mwi_event_cb(const struct ast_event *event, void *userdata)
 {
-       /* Update time on device */
-       handle_time_date_req_message(NULL, s);
+       struct skinny_line *l = userdata;
+       struct skinny_device *d = l->device;
+       if (d) {
+               struct skinnysession *s = d->session;
+               struct skinny_line *l2;
+               int new_msgs = 0;
+               int dev_msgs = 0;
+
+               if (s) {
+                       if (event) {
+                               l->newmsgs = ast_event_get_ie_uint(event, AST_EVENT_IE_NEWMSGS);
+                       }
+
+                       if (l->newmsgs) {
+                               transmit_lamp_indication(d, STIMULUS_VOICEMAIL, l->instance, l->mwiblink?SKINNY_LAMP_BLINK:SKINNY_LAMP_ON);
+                       } else {
+                               transmit_lamp_indication(d, STIMULUS_VOICEMAIL, l->instance, SKINNY_LAMP_OFF);
+                       }
+
+                       /* find out wether the device lamp should be on or off */
+                       AST_LIST_TRAVERSE(&d->lines, l2, list) {
+                               if (l2->newmsgs) {
+                                       dev_msgs++;
+                               }
+                       }
+
+                       if (dev_msgs) {
+                               transmit_lamp_indication(d, STIMULUS_VOICEMAIL, 0, d->mwiblink?SKINNY_LAMP_BLINK:SKINNY_LAMP_ON);
+                       } else {
+                               transmit_lamp_indication(d, STIMULUS_VOICEMAIL, 0, SKINNY_LAMP_OFF);
+                       }
+                       ast_verb(3, "Skinny mwi_event_cb found %d new messages\n", new_msgs);
+               }
+       }
 }
 
 /* I do not believe skinny can deal with video.
    Anyone know differently? */
 /* Yes, it can.  Currently 7985 and Cisco VT Advantage do video. */
-static enum ast_rtp_get_result skinny_get_vrtp_peer(struct ast_channel *c, struct ast_rtp **rtp)
+static enum ast_rtp_glue_result skinny_get_vrtp_peer(struct ast_channel *c, struct ast_rtp_instance **instance)
 {
        struct skinny_subchannel *sub = NULL;
 
        if (!(sub = c->tech_pvt) || !(sub->vrtp))
-               return AST_RTP_GET_FAILED;
+               return AST_RTP_GLUE_RESULT_FORBID;
 
-       *rtp = sub->vrtp;
+       ao2_ref(sub->vrtp, +1);
+       *instance = sub->vrtp;
 
-       return AST_RTP_TRY_NATIVE;
+       return AST_RTP_GLUE_RESULT_REMOTE;
 }
 
-static enum ast_rtp_get_result skinny_get_rtp_peer(struct ast_channel *c, struct ast_rtp **rtp)
+static enum ast_rtp_glue_result skinny_get_rtp_peer(struct ast_channel *c, struct ast_rtp_instance **instance)
 {
        struct skinny_subchannel *sub = NULL;
        struct skinny_line *l;
-       enum ast_rtp_get_result res = AST_RTP_TRY_NATIVE;
+       enum ast_rtp_glue_result res = AST_RTP_GLUE_RESULT_REMOTE;
 
        if (skinnydebug)
                ast_verb(1, "skinny_get_rtp_peer() Channel = %s\n", c->name);
 
 
        if (!(sub = c->tech_pvt))
-               return AST_RTP_GET_FAILED;
+               return AST_RTP_GLUE_RESULT_FORBID;
 
        ast_mutex_lock(&sub->lock);
 
        if (!(sub->rtp)){
                ast_mutex_unlock(&sub->lock);
-               return AST_RTP_GET_FAILED;
+               return AST_RTP_GLUE_RESULT_FORBID;
        }
-       
-       *rtp = sub->rtp;
+
+       ao2_ref(sub->rtp, +1);
+       *instance = sub->rtp;
 
        l = sub->parent;
 
-       if (!l->canreinvite || l->nat){
-               res = AST_RTP_TRY_PARTIAL;
+       if (!l->directmedia || l->nat){
+               res = AST_RTP_GLUE_RESULT_LOCAL;
                if (skinnydebug)
-                       ast_verb(1, "skinny_get_rtp_peer() Using AST_RTP_TRY_PARTIAL \n");
+                       ast_verb(1, "skinny_get_rtp_peer() Using AST_RTP_GLUE_RESULT_LOCAL \n");
        }
 
        ast_mutex_unlock(&sub->lock);
@@ -2599,15 +2704,15 @@ static enum ast_rtp_get_result skinny_get_rtp_peer(struct ast_channel *c, struct
 
 }
 
-static int skinny_set_rtp_peer(struct ast_channel *c, struct ast_rtp *rtp, struct ast_rtp *vrtp, struct ast_rtp *trtp, int codecs, int nat_active)
+static int skinny_set_rtp_peer(struct ast_channel *c, struct ast_rtp_instance *rtp, struct ast_rtp_instance *vrtp, struct ast_rtp_instance *trtp, format_t codecs, int nat_active)
 {
        struct skinny_subchannel *sub;
        struct skinny_line *l;
        struct skinny_device *d;
        struct skinnysession *s;
        struct ast_format_list fmt;
-       struct sockaddr_in us;
-       struct sockaddr_in them;
+       struct sockaddr_in us = { 0, };
+       struct sockaddr_in them = { 0, };
        struct skinny_req *req;
        
        sub = c->tech_pvt;
@@ -2624,7 +2729,7 @@ static int skinny_set_rtp_peer(struct ast_channel *c, struct ast_rtp *rtp, struc
        s = d->session;
 
        if (rtp){
-               ast_rtp_get_peer(rtp, &them);
+               ast_rtp_instance_get_remote_address(rtp, &them);
 
                /* Shutdown any early-media or previous media on re-invite */
                if (!(req = req_alloc(sizeof(struct stop_media_transmission_message), STOP_MEDIA_TRANSMISSION_MESSAGE)))
@@ -2643,12 +2748,12 @@ static int skinny_set_rtp_peer(struct ast_channel *c, struct ast_rtp *rtp, struc
                fmt = ast_codec_pref_getsize(&l->prefs, ast_best_codec(l->capability));
 
                if (skinnydebug)
-                       ast_verb(1, "Setting payloadType to '%d' (%d ms)\n", fmt.bits, fmt.cur_ms);
+                       ast_verb(1, "Setting payloadType to '%s' (%d ms)\n", ast_getformatname(fmt.bits), fmt.cur_ms);
 
                req->data.startmedia.conferenceId = htolel(sub->callid);
                req->data.startmedia.passThruPartyId = htolel(sub->callid);
-               if (!(l->canreinvite) || (l->nat)){
-                       ast_rtp_get_us(rtp, &us);
+               if (!(l->directmedia) || (l->nat)){
+                       ast_rtp_instance_get_local_address(rtp, &us);
                        req->data.startmedia.remoteIp = htolel(d->ourip.s_addr);
                        req->data.startmedia.remotePort = htolel(ntohs(us.sin_port));
                } else {
@@ -2669,11 +2774,11 @@ static int skinny_set_rtp_peer(struct ast_channel *c, struct ast_rtp *rtp, struc
        return 0;
 }
 
-static struct ast_rtp_protocol skinny_rtp = {
+static struct ast_rtp_glue skinny_rtp_glue = {
        .type = "Skinny",
        .get_rtp_info = skinny_get_rtp_peer,
        .get_vrtp_info = skinny_get_vrtp_peer,
-       .set_rtp_peer = skinny_set_rtp_peer,
+       .update_peer = skinny_set_rtp_peer,
 };
 
 static char *handle_skinny_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
@@ -2718,6 +2823,27 @@ static char *handle_skinny_set_debug(struct ast_cli_entry *e, int cmd, struct as
        }
 }
 
+static char *handle_skinny_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+       switch (cmd) {
+       case CLI_INIT:
+               e->command = "skinny reload";
+               e->usage =
+                       "Usage: skinny reload\n"
+                       "       Reloads the chan_skinny configuration\n";
+               return NULL;
+       case CLI_GENERATE:
+               return NULL;
+       }
+       
+       if (a->argc != e->args)
+               return CLI_SHOWUSAGE;
+
+       skinny_reload();
+       return CLI_SUCCESS;
+
+}
+
 static char *complete_skinny_devices(const char *word, int state)
 {
        struct skinny_device *d;
@@ -2914,109 +3040,206 @@ static void print_codec_to_cli(int fd, struct ast_codec_pref *pref)
                ast_cli(fd, "none");
 }
 
-static char *handle_skinny_show_devices(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+static char *_skinny_show_devices(int fd, int *total, struct mansession *s, const struct message *m, int argc, const char *argv[])
 {
        struct skinny_device *d;
        struct skinny_line *l;
+       const char *id;
+       char idtext[256] = "";
+       int total_devices = 0;
 
-       switch (cmd) {
-       case CLI_INIT:
-               e->command = "skinny show devices";
-               e->usage =
-                       "Usage: skinny show devices\n"
-                       "       Lists all devices known to the Skinny subsystem.\n";
-               return NULL;
-       case CLI_GENERATE:
-               return NULL;
+       if (s) {        /* Manager - get ActionID */
+               id = astman_get_header(m, "ActionID");
+               if (!ast_strlen_zero(id))
+                       snprintf(idtext, sizeof(idtext), "ActionID: %s\r\n", id);
        }
 
-       if (a->argc != 3)
+       switch (argc) {
+       case 3:
+               break;
+       default:
                return CLI_SHOWUSAGE;
+       }
 
-       ast_cli(a->fd, "Name                 DeviceId         IP              Type            R NL\n");
-       ast_cli(a->fd, "-------------------- ---------------- --------------- --------------- - --\n");
+       if (!s) {
+               ast_cli(fd, "Name                 DeviceId         IP              Type            R NL\n");
+               ast_cli(fd, "-------------------- ---------------- --------------- --------------- - --\n");
+       }
 
-       AST_LIST_LOCK(&devices); 
+       AST_LIST_LOCK(&devices);
        AST_LIST_TRAVERSE(&devices, d, list) {
                int numlines = 0;
+               total_devices++;
                AST_LIST_TRAVERSE(&d->lines, l, list) {
                        numlines++;
                }
-               
-               ast_cli(a->fd, "%-20s %-16s %-15s %-15s %c %2d\n",
-                       d->name,
-                       d->id,
-                       d->session?ast_inet_ntoa(d->session->sin.sin_addr):"",
-                       device2str(d->type),
-                       d->registered?'Y':'N',
-                       numlines);
+               if (!s) {
+                       ast_cli(fd, "%-20s %-16s %-15s %-15s %c %2d\n",
+                               d->name,
+                               d->id,
+                               d->session?ast_inet_ntoa(d->session->sin.sin_addr):"",
+                               device2str(d->type),
+                               d->registered?'Y':'N',
+                               numlines);
+               } else {
+                       astman_append(s,
+                               "Event: DeviceEntry\r\n%s"
+                               "Channeltype: SKINNY\r\n"
+                               "ObjectName: %s\r\n"
+                               "ChannelObjectType: device\r\n"
+                               "DeviceId: %s\r\n"
+                               "IPaddress: %s\r\n"
+                               "Type: %s\r\n"
+                               "Devicestatus: %s\r\n"
+                               "NumberOfLines: %d\r\n",
+                               idtext,
+                               d->name,
+                               d->id,
+                               d->session?ast_inet_ntoa(d->session->sin.sin_addr):"-none-",
+                               device2str(d->type),
+                               d->registered?"registered":"unregistered",
+                               numlines);
+               }
        }
        AST_LIST_UNLOCK(&devices);
+
+       if (total)
+               *total = total_devices;
+       
        return CLI_SUCCESS;
 }
 
-/*! \brief Show device information */
-static char *handle_skinny_show_device(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+/*! \brief  Show SKINNY devices in the manager API */
+/*    Inspired from chan_sip */
+static int manager_skinny_show_devices(struct mansession *s, const struct message *m)
+{
+       const char *id = astman_get_header(m, "ActionID");
+       const char *a[] = {"skinny", "show", "devices"};
+       char idtext[256] = "";
+       int total = 0;
+
+       if (!ast_strlen_zero(id))
+               snprintf(idtext, sizeof(idtext), "ActionID: %s\r\n", id);
+
+       astman_send_listack(s, m, "Device status list will follow", "start");
+       /* List the devices in separate manager events */
+       _skinny_show_devices(-1, &total, s, m, 3, a);
+       /* Send final confirmation */
+       astman_append(s,
+       "Event: DevicelistComplete\r\n"
+       "EventList: Complete\r\n"
+       "ListItems: %d\r\n"
+       "%s"
+       "\r\n", total, idtext);
+       return 0;
+}
+
+static char *handle_skinny_show_devices(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
-       struct skinny_device *d;
-       struct skinny_line *l;
-       struct skinny_speeddial *sd;
-       struct skinny_addon *sa;
-       char codec_buf[512];
 
        switch (cmd) {
        case CLI_INIT:
-               e->command = "skinny show device";
+               e->command = "skinny show devices";
                e->usage =
-                       "Usage: skinny show device <DeviceId|DeviceName>\n"
-                       "       Lists all deviceinformation of a specific device known to the Skinny subsystem.\n";
+                       "Usage: skinny show devices\n"
+                       "       Lists all devices known to the Skinny subsystem.\n";
                return NULL;
        case CLI_GENERATE:
-               return complete_skinny_show_device(a->line, a->word, a->pos, a->n);
+               return NULL;
        }
 
-       if (a->argc < 4)
+       return _skinny_show_devices(a->fd, NULL, NULL, NULL, a->argc, (const char **) a->argv);
+}
+
+static char *_skinny_show_device(int type, int fd, struct mansession *s, const struct message *m, int argc, const char *argv[])
+{
+       struct skinny_device *d;
+       struct skinny_line *l;
+       struct skinny_speeddial *sd;
+       struct skinny_addon *sa;
+       char codec_buf[512];
+
+       if (argc < 4) {
                return CLI_SHOWUSAGE;
+       }
 
        AST_LIST_LOCK(&devices);
        AST_LIST_TRAVERSE(&devices, d, list) {
-               if (!strcasecmp(a->argv[3], d->id) || !strcasecmp(a->argv[3], d->name)) {
+               if (!strcasecmp(argv[3], d->id) || !strcasecmp(argv[3], d->name)) {
                        int numlines = 0, numaddons = 0, numspeeddials = 0;
 
                        AST_LIST_TRAVERSE(&d->lines, l, list){
                                numlines++;
                        }
 
-                       ast_cli(a->fd, "Name:        %s\n", d->name);
-                       ast_cli(a->fd, "Id:          %s\n", d->id);
-                       ast_cli(a->fd, "version:     %s\n", S_OR(d->version_id, "Unknown"));
-                       ast_cli(a->fd, "Ip address:  %s\n", (d->session ? ast_inet_ntoa(d->session->sin.sin_addr) : "Unknown"));
-                       ast_cli(a->fd, "Port:        %d\n", (d->session ? ntohs(d->session->sin.sin_port) : 0));
-                       ast_cli(a->fd, "Device Type: %s\n", device2str(d->type));
-                       ast_cli(a->fd, "Conf Codecs:");
-                       ast_getformatname_multiple(codec_buf, sizeof(codec_buf) - 1, d->confcapability);
-                       ast_cli(a->fd, "%s\n", codec_buf);
-                       ast_cli(a->fd, "Neg Codecs: ");
-                       ast_getformatname_multiple(codec_buf, sizeof(codec_buf) - 1, d->capability);
-                       ast_cli(a->fd, "%s\n", codec_buf);
-                       ast_cli(a->fd, "Registered:  %s\n", (d->registered ? "Yes" : "No"));
-                       ast_cli(a->fd, "Lines:       %d\n", numlines);
-                       AST_LIST_TRAVERSE(&d->lines, l, list) {
-                               ast_cli(a->fd, "  %s (%s)\n", l->name, l->label);
-                       }
                        AST_LIST_TRAVERSE(&d->addons, sa, list) {
                                numaddons++;
-                       }       
-                       ast_cli(a->fd, "Addons:      %d\n", numaddons);
-                       AST_LIST_TRAVERSE(&d->addons, sa, list) {
-                               ast_cli(a->fd, "  %s\n", sa->type);
                        }
+
                        AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
                                numspeeddials++;
                        }
-                       ast_cli(a->fd, "Speeddials:  %d\n", numspeeddials);
-                       AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
-                               ast_cli(a->fd, "  %s (%s) ishint: %d\n", sd->exten, sd->label, sd->isHint);
+
+                       if (type == 0) { /* CLI */
+                               ast_cli(fd, "Name:        %s\n", d->name);
+                               ast_cli(fd, "Id:          %s\n", d->id);
+                               ast_cli(fd, "version:     %s\n", S_OR(d->version_id, "Unknown"));
+                               ast_cli(fd, "Ip address:  %s\n", (d->session ? ast_inet_ntoa(d->session->sin.sin_addr) : "Unknown"));
+                               ast_cli(fd, "Port:        %d\n", (d->session ? ntohs(d->session->sin.sin_port) : 0));
+                               ast_cli(fd, "Device Type: %s\n", device2str(d->type));
+                               ast_cli(fd, "Conf Codecs:");
+                               ast_getformatname_multiple(codec_buf, sizeof(codec_buf) - 1, d->confcapability);
+                               ast_cli(fd, "%s\n", codec_buf);
+                               ast_cli(fd, "Neg Codecs: ");
+                               ast_getformatname_multiple(codec_buf, sizeof(codec_buf) - 1, d->capability);
+                               ast_cli(fd, "%s\n", codec_buf);
+                               ast_cli(fd, "Registered:  %s\n", (d->registered ? "Yes" : "No"));
+                               ast_cli(fd, "Lines:       %d\n", numlines);
+                               AST_LIST_TRAVERSE(&d->lines, l, list) {
+                                       ast_cli(fd, "  %s (%s)\n", l->name, l->label);
+                               }
+                               AST_LIST_TRAVERSE(&d->addons, sa, list) {
+                                       numaddons++;
+                               }       
+                               ast_cli(fd, "Addons:      %d\n", numaddons);
+                               AST_LIST_TRAVERSE(&d->addons, sa, list) {
+                                       ast_cli(fd, "  %s\n", sa->type);
+                               }
+                               AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
+                                       numspeeddials++;
+                               }
+                               ast_cli(fd, "Speeddials:  %d\n", numspeeddials);
+                               AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
+                                       ast_cli(fd, "  %s (%s) ishint: %d\n", sd->exten, sd->label, sd->isHint);
+                               }
+                       } else { /* manager */
+                               astman_append(s, "Channeltype: SKINNY\r\n");
+                               astman_append(s, "ObjectName: %s\r\n", d->name);
+                               astman_append(s, "ChannelObjectType: device\r\n");
+                               astman_append(s, "Id: %s\r\n", d->id);
+                               astman_append(s, "version: %s\r\n", S_OR(d->version_id, "Unknown"));
+                               astman_append(s, "Ipaddress: %s\r\n", (d->session ? ast_inet_ntoa(d->session->sin.sin_addr) : "Unknown"));
+                               astman_append(s, "Port: %d\r\n", (d->session ? ntohs(d->session->sin.sin_port) : 0));
+                               astman_append(s, "DeviceType: %s\r\n", device2str(d->type));
+                               astman_append(s, "Codecs: ");
+                               ast_getformatname_multiple(codec_buf, sizeof(codec_buf) -1, d->confcapability);
+                               astman_append(s, "%s\r\n", codec_buf);
+                               astman_append(s, "CodecOrder: ");
+                               ast_getformatname_multiple(codec_buf, sizeof(codec_buf) -1, d->capability);
+                               astman_append(s, "%s\r\n", codec_buf);
+                               astman_append(s, "Devicestatus: %s\r\n", (d->registered?"registered":"unregistered"));
+                               astman_append(s, "NumberOfLines: %d\r\n", numlines);
+                               AST_LIST_TRAVERSE(&d->lines, l, list) {
+                                       astman_append(s, "Line: %s (%s)\r\n", l->name, l->label);
+                               }
+                               astman_append(s, "NumberOfAddons: %d\r\n", numaddons);
+                               AST_LIST_TRAVERSE(&d->addons, sa, list) {
+                                       astman_append(s, "Addon: %s\r\n", sa->type);
+                               }
+                               astman_append(s, "NumberOfSpeeddials: %d\r\n", numspeeddials);
+                               AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
+                                       astman_append(s, "Speeddial: %s (%s) ishint: %d\r\n", sd->exten, sd->label, sd->isHint);
+                               }
                        }
                }
        }
@@ -3024,10 +3247,143 @@ static char *handle_skinny_show_device(struct ast_cli_entry *e, int cmd, struct
        return CLI_SUCCESS;
 }
 
-static char *handle_skinny_show_lines(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+static int manager_skinny_show_device(struct mansession *s, const struct message *m)
+{
+       const char *a[4];
+       const char *device;
+
+       device = astman_get_header(m, "Device");
+       if (ast_strlen_zero(device)) {
+               astman_send_error(s, m, "Device: <name> missing.");
+               return 0;
+       }
+       a[0] = "skinny";
+       a[1] = "show";
+       a[2] = "device";
+       a[3] = device;
+
+       _skinny_show_device(1, -1, s, m, 4, a);
+       astman_append(s, "\r\n\r\n" );
+       return 0;
+}
+
+/*! \brief Show device information */
+static char *handle_skinny_show_device(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+       switch (cmd) {
+       case CLI_INIT:
+               e->command = "skinny show device";
+               e->usage =
+                       "Usage: skinny show device <DeviceId|DeviceName>\n"
+                       "       Lists all deviceinformation of a specific device known to the Skinny subsystem.\n";
+               return NULL;
+       case CLI_GENERATE:
+               return complete_skinny_show_device(a->line, a->word, a->pos, a->n);
+       }
+
+       return _skinny_show_device(0, a->fd, NULL, NULL, a->argc, (const char **) a->argv);
+}
+
+static char *_skinny_show_lines(int fd, int *total, struct mansession *s, const struct message *m, int argc, const char *argv[])
 {
        struct skinny_line *l;
        struct skinny_subchannel *sub;
+       int total_lines = 0;
+       int verbose = 0;
+       const char *id;
+       char idtext[256] = "";
+
+       if (s) {        /* Manager - get ActionID */
+               id = astman_get_header(m, "ActionID");
+               if (!ast_strlen_zero(id))
+                       snprintf(idtext, sizeof(idtext), "ActionID: %s\r\n", id);
+       }
+
+       switch (argc) {
+       case 4:
+               verbose = 1;
+               break;
+       case 3:
+               verbose = 0;
+               break;
+       default:
+               return CLI_SHOWUSAGE;
+       }
+
+       if (!s) {
+               ast_cli(fd, "Name                 Device Name          Instance Label               \n");
+               ast_cli(fd, "-------------------- -------------------- -------- --------------------\n");
+       }
+       AST_LIST_LOCK(&lines);
+       AST_LIST_TRAVERSE(&lines, l, all) {
+               total_lines++;
+               if (!s) {
+                       ast_cli(fd, "%-20s %-20s %8d %-20s\n",
+                               l->name,
+                               (l->device ? l->device->name : "Not connected"),
+                               l->instance,
+                               l->label);
+                       if (verbose) {
+                               AST_LIST_TRAVERSE(&l->sub, sub, list) {
+                                       ast_cli(fd, "  %s> %s to %s\n",
+                                               (sub == l->activesub?"Active  ":"Inactive"),
+                                               sub->owner->name,
+                                               (ast_bridged_channel(sub->owner)?ast_bridged_channel(sub->owner)->name:"")
+                                       );
+                               }
+                       }
+               } else {
+                       astman_append(s,
+                               "Event: LineEntry\r\n%s"
+                               "Channeltype: SKINNY\r\n"
+                               "ObjectName: %s\r\n"
+                               "ChannelObjectType: line\r\n"
+                               "Device: %s\r\n"
+                               "Instance: %d\r\n"
+                               "Label: %s\r\n",
+                               idtext,
+                               l->name,
+                               (l->device?l->device->name:"None"),
+                               l->instance,
+                               l->label);
+               }
+               AST_LIST_UNLOCK(&lines);
+       }
+
+       if (total) {
+               *total = total_lines;
+       }
+
+       return CLI_SUCCESS;
+}
+
+/*! \brief  Show Skinny lines in the manager API */
+/*    Inspired from chan_sip */
+static int manager_skinny_show_lines(struct mansession *s, const struct message *m)
+{
+       const char *id = astman_get_header(m, "ActionID");
+       const char *a[] = {"skinny", "show", "lines"};
+       char idtext[256] = "";
+       int total = 0;
+
+       if (!ast_strlen_zero(id))
+               snprintf(idtext, sizeof(idtext), "ActionID: %s\r\n", id);
+
+       astman_send_listack(s, m, "Line status list will follow", "start");
+       /* List the lines in separate manager events */
+       _skinny_show_lines(-1, &total, s, m, 3, a);
+       /* Send final confirmation */
+       astman_append(s,
+       "Event: LinelistComplete\r\n"
+       "EventList: Complete\r\n"
+       "ListItems: %d\r\n"
+       "%s"
+       "\r\n", total, idtext);
+       return 0;
+}
+
+static char *handle_skinny_show_lines(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
        int verbose = 0;
 
        switch (cmd) {
@@ -3043,7 +3399,6 @@ static char *handle_skinny_show_lines(struct ast_cli_entry *e, int cmd, struct a
                return NULL;
        }
 
-
        if (a->argc == e->args) {
                if (!strcasecmp(a->argv[e->args-1], "verbose")) {
                        verbose = 1;
@@ -3053,105 +3408,135 @@ static char *handle_skinny_show_lines(struct ast_cli_entry *e, int cmd, struct a
        } else if (a->argc != e->args - 1) {
                return CLI_SHOWUSAGE;
        }
-       
-       
-       ast_cli(a->fd, "Name                 Device Name          Instance Label               \n");
-       ast_cli(a->fd, "-------------------- -------------------- -------- --------------------\n");
-       AST_LIST_LOCK(&lines);
-       AST_LIST_TRAVERSE(&lines, l, all) {
-               ast_cli(a->fd, "%-20s %-20s %8d %-20s\n",
-                       l->name,
-                       (l->device ? l->device->name : "Not connected"),
-                       l->instance,
-                       l->label);
-                       if (verbose) {
-                       AST_LIST_TRAVERSE(&l->sub, sub, list) {
-                               ast_cli(a->fd, "  %s> %s to %s\n",
-                                       (sub == l->activesub?"Active  ":"Inactive"),
-                                       sub->owner->name,
-                                       (ast_bridged_channel(sub->owner)?ast_bridged_channel(sub->owner)->name:"")
-                               );
-                       }
-               }
-       }
-       AST_LIST_UNLOCK(&lines);
-       return CLI_SUCCESS;
+
+       return _skinny_show_lines(a->fd, NULL, NULL, NULL, a->argc, (const char **) a->argv);
 }
 
-/*! \brief List line information. */
-static char *handle_skinny_show_line(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+static char *_skinny_show_line(int type, int fd, struct mansession *s, const struct message *m, int argc, const char *argv[])
 {
        struct skinny_device *d;
        struct skinny_line *l;
+       struct ast_codec_pref *pref;
+       int x = 0, codec = 0;
        char codec_buf[512];
        char group_buf[256];
+       char cbuf[256];
 
-       switch (cmd) {
-       case CLI_INIT:
-               e->command = "skinny show line";
-               e->usage =
-                       "Usage: skinny show line <Line> [ on <DeviceID|DeviceName> ]\n"
-                       "       List all lineinformation of a specific line known to the Skinny subsystem.\n";
-               return NULL;
-       case CLI_GENERATE:
-               return complete_skinny_show_line(a->line, a->word, a->pos, a->n);
+       switch (argc) {
+       case 4:
+               break;
+       case 6:
+               break;
+       default:
+               return CLI_SHOWUSAGE;
        }
 
-       if (a->argc < 4)
-               return CLI_SHOWUSAGE;
-       
        AST_LIST_LOCK(&devices);
 
        /* Show all lines matching the one supplied */
        AST_LIST_TRAVERSE(&devices, d, list) {
-               if (a->argc == 6 && (strcasecmp(a->argv[5], d->id) && strcasecmp(a->argv[5], d->name)))
+               if (argc == 6 && (strcasecmp(argv[5], d->id) && strcasecmp(argv[5], d->name))) {
                        continue;
+               }
                AST_LIST_TRAVERSE(&d->lines, l, list) {
-                       if (strcasecmp(a->argv[3], l->name))
+                       if (strcasecmp(argv[3], l->name)) {
                                continue;
-                       ast_cli(a->fd, "Line:             %s\n", l->name);
-                       ast_cli(a->fd, "On Device:        %s\n", d->name);
-                       ast_cli(a->fd, "Line Label:       %s\n", l->label);
-                       ast_cli(a->fd, "Extension:        %s\n", S_OR(l->exten, "<not set>"));
-                       ast_cli(a->fd, "Context:          %s\n", l->context);
-                       ast_cli(a->fd, "CallGroup:        %s\n", ast_print_group(group_buf, sizeof(group_buf), l->callgroup));
-                       ast_cli(a->fd, "PickupGroup:      %s\n", ast_print_group(group_buf, sizeof(group_buf), l->pickupgroup));
-                       ast_cli(a->fd, "Language:         %s\n", S_OR(l->language, "<not set>"));
-                       ast_cli(a->fd, "Accountcode:      %s\n", S_OR(l->accountcode, "<not set>"));
-                       ast_cli(a->fd, "AmaFlag:          %s\n", ast_cdr_flags2str(l->amaflags));
-                       ast_cli(a->fd, "CallerId Number:  %s\n", S_OR(l->cid_num, "<not set>"));
-                       ast_cli(a->fd, "CallerId Name:    %s\n", S_OR(l->cid_name, "<not set>"));
-                       ast_cli(a->fd, "Hide CallerId:    %s\n", (l->hidecallerid ? "Yes" : "No"));
-                       ast_cli(a->fd, "CFwdAll:          %s\n", S_COR((l->cfwdtype & SKINNY_CFWD_ALL), l->call_forward_all, "<not set>"));
-                       ast_cli(a->fd, "CFwdBusy:         %s\n", S_COR((l->cfwdtype & SKINNY_CFWD_BUSY), l->call_forward_busy, "<not set>"));
-                       ast_cli(a->fd, "CFwdNoAnswer:     %s\n", S_COR((l->cfwdtype & SKINNY_CFWD_NOANSWER), l->call_forward_noanswer, "<not set>"));
-                       ast_cli(a->fd, "VoicemailBox:     %s\n", S_OR(l->mailbox, "<not set>"));
-                       ast_cli(a->fd, "VoicemailNumber:  %s\n", S_OR(l->vmexten, "<not set>"));
-                       ast_cli(a->fd, "MWIblink:         %d\n", l->mwiblink);
-                       ast_cli(a->fd, "Regextension:     %s\n", S_OR(l->regexten, "<not set>"));
-                       ast_cli(a->fd, "Regcontext:       %s\n", S_OR(l->regcontext, "<not set>"));
-                       ast_cli(a->fd, "MoHInterpret:     %s\n", S_OR(l->mohinterpret, "<not set>"));
-                       ast_cli(a->fd, "MoHSuggest:       %s\n", S_OR(l->mohsuggest, "<not set>"));
-                       ast_cli(a->fd, "Last dialed nr:   %s\n", S_OR(l->lastnumberdialed, "<no calls made yet>"));
-                       ast_cli(a->fd, "Last CallerID:    %s\n", S_OR(l->lastcallerid, "<not set>"));
-                       ast_cli(a->fd, "Transfer enabled: %s\n", (l->transfer ? "Yes" : "No"));
-                       ast_cli(a->fd, "Callwaiting:      %s\n", (l->callwaiting ? "Yes" : "No"));
-                       ast_cli(a->fd, "3Way Calling:     %s\n", (l->threewaycalling ? "Yes" : "No"));
-                       ast_cli(a->fd, "Can forward:      %s\n", (l->cancallforward ? "Yes" : "No"));
-                       ast_cli(a->fd, "Do Not Disturb:   %s\n", (l->dnd ? "Yes" : "No"));
-                       ast_cli(a->fd, "NAT:              %s\n", (l->nat ? "Yes" : "No"));
-                       ast_cli(a->fd, "immediate:        %s\n", (l->immediate ? "Yes" : "No"));
-                       ast_cli(a->fd, "Group:            %d\n", l->group);
-                       ast_cli(a->fd, "Conf Codecs:      ");
-                       ast_getformatname_multiple(codec_buf, sizeof(codec_buf) - 1, l->confcapability);
-                       ast_cli(a->fd, "%s\n", codec_buf);
-                       ast_cli(a->fd, "Neg Codecs:       ");
-                       ast_getformatname_multiple(codec_buf, sizeof(codec_buf) - 1, l->capability);
-                       ast_cli(a->fd, "%s\n", codec_buf);
-                       ast_cli(a->fd, "Codec Order:      (");
-                       print_codec_to_cli(a->fd, &l->prefs);
-                       ast_cli(a->fd, ")\n");
-                       ast_cli(a->fd, "\n");
+                       }
+                       if (type == 0) { /* CLI */
+                               ast_cli(fd, "Line:             %s\n", l->name);
+                               ast_cli(fd, "On Device:        %s\n", d->name);
+                               ast_cli(fd, "Line Label:       %s\n", l->label);
+                               ast_cli(fd, "Extension:        %s\n", S_OR(l->exten, "<not set>"));
+                               ast_cli(fd, "Context:          %s\n", l->context);
+                               ast_cli(fd, "CallGroup:        %s\n", ast_print_group(group_buf, sizeof(group_buf), l->callgroup));
+                               ast_cli(fd, "PickupGroup:      %s\n", ast_print_group(group_buf, sizeof(group_buf), l->pickupgroup));
+                               ast_cli(fd, "Language:         %s\n", S_OR(l->language, "<not set>"));
+                               ast_cli(fd, "Accountcode:      %s\n", S_OR(l->accountcode, "<not set>"));
+                               ast_cli(fd, "AmaFlag:          %s\n", ast_cdr_flags2str(l->amaflags));
+                               ast_cli(fd, "CallerId Number:  %s\n", S_OR(l->cid_num, "<not set>"));
+                               ast_cli(fd, "CallerId Name:    %s\n", S_OR(l->cid_name, "<not set>"));
+                               ast_cli(fd, "Hide CallerId:    %s\n", (l->hidecallerid ? "Yes" : "No"));
+                               ast_cli(fd, "CFwdAll:          %s\n", S_COR((l->cfwdtype & SKINNY_CFWD_ALL), l->call_forward_all, "<not set>"));
+                               ast_cli(fd, "CFwdBusy:         %s\n", S_COR((l->cfwdtype & SKINNY_CFWD_BUSY), l->call_forward_busy, "<not set>"));
+                               ast_cli(fd, "CFwdNoAnswer:     %s\n", S_COR((l->cfwdtype & SKINNY_CFWD_NOANSWER), l->call_forward_noanswer, "<not set>"));
+                               ast_cli(fd, "VoicemailBox:     %s\n", S_OR(l->mailbox, "<not set>"));
+                               ast_cli(fd, "VoicemailNumber:  %s\n", S_OR(l->vmexten, "<not set>"));
+                               ast_cli(fd, "MWIblink:         %d\n", l->mwiblink);
+                               ast_cli(fd, "Regextension:     %s\n", S_OR(l->regexten, "<not set>"));
+                               ast_cli(fd, "Regcontext:       %s\n", S_OR(l->regcontext, "<not set>"));
+                               ast_cli(fd, "MoHInterpret:     %s\n", S_OR(l->mohinterpret, "<not set>"));
+                               ast_cli(fd, "MoHSuggest:       %s\n", S_OR(l->mohsuggest, "<not set>"));
+                               ast_cli(fd, "Last dialed nr:   %s\n", S_OR(l->lastnumberdialed, "<no calls made yet>"));
+                               ast_cli(fd, "Last CallerID:    %s\n", S_OR(l->lastcallerid, "<not set>"));
+                               ast_cli(fd, "Transfer enabled: %s\n", (l->transfer ? "Yes" : "No"));
+                               ast_cli(fd, "Callwaiting:      %s\n", (l->callwaiting ? "Yes" : "No"));
+                               ast_cli(fd, "3Way Calling:     %s\n", (l->threewaycalling ? "Yes" : "No"));
+                               ast_cli(fd, "Can forward:      %s\n", (l->cancallforward ? "Yes" : "No"));
+                               ast_cli(fd, "Do Not Disturb:   %s\n", (l->dnd ? "Yes" : "No"));
+                               ast_cli(fd, "NAT:              %s\n", (l->nat ? "Yes" : "No"));
+                               ast_cli(fd, "immediate:        %s\n", (l->immediate ? "Yes" : "No"));
+                               ast_cli(fd, "Group:            %d\n", l->group);
+                               ast_cli(fd, "Parkinglot:       %s\n", S_OR(l->parkinglot, "<not set>"));
+                               ast_cli(fd, "Conf Codecs:      ");
+                               ast_getformatname_multiple(codec_buf, sizeof(codec_buf) - 1, l->confcapability);
+                               ast_cli(fd, "%s\n", codec_buf);
+                               ast_cli(fd, "Neg Codecs:       ");
+                               ast_getformatname_multiple(codec_buf, sizeof(codec_buf) - 1, l->capability);
+                               ast_cli(fd, "%s\n", codec_buf);
+                               ast_cli(fd, "Codec Order:      (");
+                               print_codec_to_cli(fd, &l->prefs);
+                               ast_cli(fd, ")\n");
+                               ast_cli(fd, "\n");
+                       } else { /* manager */
+                               astman_append(s, "Channeltype: SKINNY\r\n");
+                               astman_append(s, "ObjectName: %s\r\n", l->name);
+                               astman_append(s, "ChannelObjectType: line\r\n");
+                               astman_append(s, "Device: %s\r\n", d->name);
+                               astman_append(s, "LineLabel: %s\r\n", l->label);
+                               astman_append(s, "Extension: %s\r\n", S_OR(l->exten, "<not set>"));
+                               astman_append(s, "Context: %s\r\n", l->context);
+                               astman_append(s, "CallGroup: %s\r\n", ast_print_group(group_buf, sizeof(group_buf), l->callgroup));
+                               astman_append(s, "PickupGroup: %s\r\n", ast_print_group(group_buf, sizeof(group_buf), l->pickupgroup));
+                               astman_append(s, "Language: %s\r\n", S_OR(l->language, "<not set>"));
+                               astman_append(s, "Accountcode: %s\r\n", S_OR(l->accountcode, "<not set>"));
+                               astman_append(s, "AMAflags: %s\r\n", ast_cdr_flags2str(l->amaflags));
+                               astman_append(s, "Callerid: %s\r\n", ast_callerid_merge(cbuf, sizeof(cbuf), l->cid_name, l->cid_num, ""));
+                               astman_append(s, "HideCallerId: %s\r\n", (l->hidecallerid ? "Yes" : "No"));
+                               astman_append(s, "CFwdAll: %s\r\n", S_COR((l->cfwdtype & SKINNY_CFWD_ALL), l->call_forward_all, "<not set>"));
+                               astman_append(s, "CFwdBusy: %s\r\n", S_COR((l->cfwdtype & SKINNY_CFWD_BUSY), l->call_forward_busy, "<not set>"));
+                               astman_append(s, "CFwdNoAnswer: %s\r\n", S_COR((l->cfwdtype & SKINNY_CFWD_NOANSWER), l->call_forward_noanswer, "<not set>"));
+                               astman_append(s, "VoicemailBox: %s\r\n", S_OR(l->mailbox, "<not set>"));
+                               astman_append(s, "VoicemailNumber: %s\r\n", S_OR(l->vmexten, "<not set>"));
+                               astman_append(s, "MWIblink: %d\r\n", l->mwiblink);
+                               astman_append(s, "RegExtension: %s\r\n", S_OR(l->regexten, "<not set>"));
+                               astman_append(s, "Regcontext: %s\r\n", S_OR(l->regcontext, "<not set>"));
+                               astman_append(s, "MoHInterpret: %s\r\n", S_OR(l->mohinterpret, "<not set>"));
+                               astman_append(s, "MoHSuggest: %s\r\n", S_OR(l->mohsuggest, "<not set>"));
+                               astman_append(s, "LastDialedNr: %s\r\n", S_OR(l->lastnumberdialed, "<no calls made yet>"));
+                               astman_append(s, "LastCallerID: %s\r\n", S_OR(l->lastcallerid, "<not set>"));
+                               astman_append(s, "Transfer: %s\r\n", (l->transfer ? "Yes" : "No"));
+                               astman_append(s, "Callwaiting: %s\r\n", (l->callwaiting ? "Yes" : "No"));
+                               astman_append(s, "3WayCalling: %s\r\n", (l->threewaycalling ? "Yes" : "No"));
+                               astman_append(s, "CanForward: %s\r\n", (l->cancallforward ? "Yes" : "No"));
+                               astman_append(s, "DoNotDisturb: %s\r\n", (l->dnd ? "Yes" : "No"));
+                               astman_append(s, "NAT: %s\r\n", (l->nat ? "Yes" : "No"));
+                               astman_append(s, "immediate: %s\r\n", (l->immediate ? "Yes" : "No"));
+                               astman_append(s, "Group: %d\r\n", l->group);
+                               astman_append(s, "Parkinglot: %s\r\n", S_OR(l->parkinglot, "<not set>"));
+                               ast_getformatname_multiple(codec_buf, sizeof(codec_buf) - 1, l->confcapability);
+                               astman_append(s, "Codecs: %s\r\n", codec_buf);
+                               astman_append(s, "CodecOrder: ");
+                               pref = &l->prefs;
+                               for(x = 0; x < 32 ; x++) {
+                                       codec = ast_codec_pref_index(pref, x);
+                                       if (!codec)
+                                               break;
+                                       astman_append(s, "%s", ast_getformatname(codec));
+                                       if (x < 31 && ast_codec_pref_index(pref, x+1))
+                                               astman_append(s, ",");
+                               }
+                               astman_append(s, "\r\n");
+                       }
                }
        }
        
@@ -3159,6 +3544,43 @@ static char *handle_skinny_show_line(struct ast_cli_entry *e, int cmd, struct as
        return CLI_SUCCESS;
 }
 
+static int manager_skinny_show_line(struct mansession *s, const struct message *m)
+{
+       const char *a[4];
+       const char *line;
+
+       line = astman_get_header(m, "Line");
+       if (ast_strlen_zero(line)) {
+               astman_send_error(s, m, "Line: <name> missing.");
+               return 0;
+       }
+       a[0] = "skinny";
+       a[1] = "show";
+       a[2] = "line";
+       a[3] = line;
+
+       _skinny_show_line(1, -1, s, m, 4, a);
+       astman_append(s, "\r\n\r\n" );
+       return 0;
+}
+
+/*! \brief List line information. */
+static char *handle_skinny_show_line(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+       switch (cmd) {
+       case CLI_INIT:
+               e->command = "skinny show line";
+               e->usage =
+                       "Usage: skinny show line <Line> [ on <DeviceID|DeviceName> ]\n"
+                       "       List all lineinformation of a specific line known to the Skinny subsystem.\n";
+               return NULL;
+       case CLI_GENERATE:
+               return complete_skinny_show_line(a->line, a->word, a->pos, a->n);
+       }
+
+       return _skinny_show_line(0, a->fd, NULL, NULL, a->argc, (const char **) a->argv);
+}
+
 /*! \brief List global settings for the Skinny subsystem. */
 static char *handle_skinny_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
@@ -3201,6 +3623,7 @@ static struct ast_cli_entry cli_skinny[] = {
        AST_CLI_DEFINE(handle_skinny_show_settings, "List global Skinny settings"),
        AST_CLI_DEFINE(handle_skinny_set_debug, "Enable/Disable Skinny debugging"),
        AST_CLI_DEFINE(handle_skinny_reset, "Reset Skinny device(s)"),
+       AST_CLI_DEFINE(handle_skinny_reload, "Reload Skinny config"),
 };
 
 static void start_rtp(struct skinny_subchannel *sub)
@@ -3211,29 +3634,36 @@ static void start_rtp(struct skinny_subchannel *sub)
 
        ast_mutex_lock(&sub->lock);
        /* Allocate the RTP */
-       sub->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr);
+       sub->rtp = ast_rtp_instance_new("asterisk", sched, &bindaddr, NULL);
        if (hasvideo)
-               sub->vrtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr);
-       
+               sub->vrtp = ast_rtp_instance_new("asterisk", sched, &bindaddr, NULL);
+
+       if (sub->rtp) {
+               ast_rtp_instance_set_prop(sub->rtp, AST_RTP_PROPERTY_RTCP, 1);
+       }
+       if (sub->vrtp) {
+               ast_rtp_instance_set_prop(sub->vrtp, AST_RTP_PROPERTY_RTCP, 1);
+       }
+
        if (sub->rtp && sub->owner) {
-               ast_channel_set_fd(sub->owner, 0, ast_rtp_fd(sub->rtp));
-               ast_channel_set_fd(sub->owner, 1, ast_rtcp_fd(sub->rtp));
+               ast_channel_set_fd(sub->owner, 0, ast_rtp_instance_fd(sub->rtp, 0));
+               ast_channel_set_fd(sub->owner, 1, ast_rtp_instance_fd(sub->rtp, 1));
        }
        if (hasvideo && sub->vrtp && sub->owner) {
-               ast_channel_set_fd(sub->owner, 2, ast_rtp_fd(sub->vrtp));
-               ast_channel_set_fd(sub->owner, 3, ast_rtcp_fd(sub->vrtp));
+               ast_channel_set_fd(sub->owner, 2, ast_rtp_instance_fd(sub->vrtp, 0));
+               ast_channel_set_fd(sub->owner, 3, ast_rtp_instance_fd(sub->vrtp, 1));
        }
        if (sub->rtp) {
-               ast_rtp_setqos(sub->rtp, qos.tos_audio, qos.cos_audio, "Skinny RTP");
-               ast_rtp_setnat(sub->rtp, l->nat);
+               ast_rtp_instance_set_qos(sub->rtp, qos.tos_audio, qos.cos_audio, "Skinny RTP");
+               ast_rtp_instance_set_prop(sub->rtp, AST_RTP_PROPERTY_NAT, l->nat);
        }
        if (sub->vrtp) {
-               ast_rtp_setqos(sub->vrtp, qos.tos_video, qos.cos_video, "Skinny VRTP");
-               ast_rtp_setnat(sub->vrtp, l->nat);
+               ast_rtp_instance_set_qos(sub->vrtp, qos.tos_video, qos.cos_video, "Skinny VRTP");
+               ast_rtp_instance_set_prop(sub->vrtp, AST_RTP_PROPERTY_NAT, l->nat);
        }
        /* Set Frame packetization */
        if (sub->rtp)
-               ast_rtp_codec_setpref(sub->rtp, &l->prefs);
+               ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(sub->rtp), sub->rtp, &l->prefs);
 
        /* Create the RTP connection */
        transmit_connect(d, sub);
@@ -3253,6 +3683,8 @@ static void *skinny_newcall(void *data)
                l->hidecallerid ? "" : l->cid_num,
                l->hidecallerid ? "" : l->cid_name,
                c->cid.cid_ani ? NULL : l->cid_num);
+       c->connected.id.number = ast_strdup(c->exten);
+       c->connected.id.name = NULL;
        ast_setstate(c, AST_STATE_RING);
        if (!sub->rtp) {
                start_rtp(sub);
@@ -3416,7 +3848,7 @@ static int skinny_call(struct ast_channel *ast, char *dest, int timeout)
        transmit_callstateonly(d, sub, SKINNY_RINGIN);
        transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_RINGIN);
        transmit_displaypromptstatus(d, "Ring-In", 0, l->instance, sub->callid);
-       transmit_callinfo(d, ast->cid.cid_name, ast->cid.cid_num, l->cid_name, l->cid_num, l->instance, sub->callid, 1);
+       transmit_callinfo(d, ast->connected.id.name, ast->connected.id.number, l->cid_name, l->cid_num, l->instance, sub->callid, 1);
        transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_BLINK);
        transmit_ringer_mode(d, SKINNY_RING_INSIDE);
 
@@ -3492,6 +3924,7 @@ static int skinny_hangup(struct ast_channel *ast)
                                transmit_stopmediatransmission(d, sub);
                                transmit_speaker_mode(d, SKINNY_SPEAKEROFF);
                                transmit_ringer_mode(d, SKINNY_RING_OFF);
+                               transmit_displaymessage(d, NULL, l->instance, sub->callid); /* clear display */
                                transmit_tone(d, SKINNY_SILENCE, l->instance, sub->callid);
                                /* we should check to see if we can start the ringer if another line is ringing */
                        }
@@ -3503,7 +3936,7 @@ static int skinny_hangup(struct ast_channel *ast)
        sub->alreadygone = 0;
        sub->outgoing = 0;
        if (sub->rtp) {
-               ast_rtp_destroy(sub->rtp);
+               ast_rtp_instance_destroy(sub->rtp);
                sub->rtp = NULL;
        }
        ast_mutex_unlock(&sub->lock);
@@ -3542,7 +3975,7 @@ static int skinny_answer(struct ast_channel *ast)
        /* order matters here...
           for some reason, transmit_callinfo must be before transmit_callstate,
           or you won't get keypad messages in some situations. */
-       transmit_callinfo(d, ast->cid.cid_name, ast->cid.cid_num, l->lastnumberdialed, l->lastnumberdialed, l->instance, sub->callid, 2);
+       transmit_callinfo(d, ast->connected.id.name, ast->connected.id.number, l->lastnumberdialed, l->lastnumberdialed, l->instance, sub->callid, 2);
        transmit_callstateonly(d, sub, SKINNY_CONNECTED);
        transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_CONNECTED);
        transmit_dialednumber(d, l->lastnumberdialed, l->instance, sub->callid);
@@ -3564,16 +3997,16 @@ static struct ast_frame *skinny_rtp_read(struct skinny_subchannel *sub)
 
        switch(ast->fdno) {
        case 0:
-               f = ast_rtp_read(sub->rtp); /* RTP Audio */
+               f = ast_rtp_instance_read(sub->rtp, 0); /* RTP Audio */
                break;
        case 1:
-               f = ast_rtcp_read(sub->rtp); /* RTCP Control Channel */
+               f = ast_rtp_instance_read(sub->rtp, 1); /* RTCP Control Channel */
                break;
        case 2:
-               f = ast_rtp_read(sub->vrtp); /* RTP Video */
+               f = ast_rtp_instance_read(sub->vrtp, 0); /* RTP Video */
                break;
        case 3:
-               f = ast_rtcp_read(sub->vrtp); /* RTCP Control Channel for video */
+               f = ast_rtp_instance_read(sub->vrtp, 1); /* RTCP Control Channel for video */
                break;
 #if 0
        case 5:
@@ -3588,9 +4021,9 @@ static struct ast_frame *skinny_rtp_read(struct skinny_subchannel *sub)
        if (ast) {
                /* We already hold the channel lock */
                if (f->frametype == AST_FRAME_VOICE) {
-                       if (f->subclass != ast->nativeformats) {
-                               ast_debug(1, "Oooh, format changed to %d\n", f->subclass);
-                               ast->nativeformats = f->subclass;
+                       if (f->subclass.codec != ast->nativeformats) {
+                               ast_debug(1, "Oooh, format changed to %s\n", ast_getformatname(f->subclass.codec));
+                               ast->nativeformats = f->subclass.codec;
                                ast_set_read_format(ast, ast->readformat);
                                ast_set_write_format(ast, ast->writeformat);
                        }
@@ -3621,16 +4054,20 @@ static int skinny_write(struct ast_channel *ast, struct ast_frame *frame)
                        return 0;
                }
        } else {
-               if (!(frame->subclass & ast->nativeformats)) {
-                       ast_log(LOG_WARNING, "Asked to transmit frame type %d, while native formats is %d (read/write = %d/%d)\n",
-                               frame->subclass, ast->nativeformats, ast->readformat, ast->writeformat);
+               if (!(frame->subclass.codec & ast->nativeformats)) {
+                       char buf[256];
+                       ast_log(LOG_WARNING, "Asked to transmit frame type %s, while native formats is %s (read/write = %s/%s)\n",
+                               ast_getformatname(frame->subclass.codec),
+                               ast_getformatname_multiple(buf, sizeof(buf), ast->nativeformats),
+                               ast_getformatname(ast->readformat),
+                               ast_getformatname(ast->writeformat));
                        return -1;
                }
        }
        if (sub) {
                ast_mutex_lock(&sub->lock);
                if (sub->rtp) {
-                       res = ast_rtp_write(sub->rtp, frame);
+                       res = ast_rtp_instance_write(sub->rtp, frame);
                }
                ast_mutex_unlock(&sub->lock);
        }
@@ -3737,6 +4174,10 @@ static char *control2str(int ind) {
                return "Unhold";
        case AST_CONTROL_SRCUPDATE:
                return "Media Source Update";
+       case AST_CONTROL_CONNECTED_LINE:
+               return "Connected Line";
+       case AST_CONTROL_REDIRECTING:
+               return "Redirecting";
        case -1:
                return "Stop tone";
        default:
@@ -3751,7 +4192,7 @@ static int skinny_transfer(struct skinny_subchannel *sub)
 {
        struct skinny_subchannel *xferor; /* the sub doing the transferring */
        struct skinny_subchannel *xferee; /* the sub being transferred */
-       const struct tone_zone_sound *ts = NULL;
+       struct ast_tone_zone_sound *ts = NULL;
                
        if (ast_bridged_channel(sub->owner) || ast_bridged_channel(sub->related->owner)) {
                if (sub->xferor) {
@@ -3774,8 +4215,10 @@ static int skinny_transfer(struct skinny_subchannel *sub)
                        }
                        if (xferor->owner->_state == AST_STATE_RING) {
                                /* play ringing inband */
-                               ts = ast_get_indication_tone(xferor->owner->zone, "ring");
-                               ast_playtones_start(xferor->owner, 0, ts->data, 1);
+                               if ((ts = ast_get_indication_tone(xferor->owner->zone, "ring"))) {
+                                       ast_playtones_start(xferor->owner, 0, ts->data, 1);
+                                       ts = ast_tone_zone_sound_unref(ts);
+                               }
                        }
                        if (skinnydebug)
                                ast_debug(1, "Transfer Masquerading %s to %s\n",
@@ -3789,8 +4232,10 @@ static int skinny_transfer(struct skinny_subchannel *sub)
                        ast_queue_control(xferee->owner, AST_CONTROL_UNHOLD);
                        if (xferor->owner->_state == AST_STATE_RING) {
                                /* play ringing inband */
-                               ts = ast_get_indication_tone(xferor->owner->zone, "ring");
-                               ast_playtones_start(xferor->owner, 0, ts->data, 1);
+                               if ((ts = ast_get_indication_tone(xferor->owner->zone, "ring"))) {
+                                       ast_playtones_start(xferor->owner, 0, ts->data, 1);
+                                       ts = ast_tone_zone_sound_unref(ts);
+                               }
                        }
                        if (skinnydebug)
                                ast_debug(1, "Transfer Masquerading %s to %s\n",
@@ -3840,7 +4285,7 @@ static int skinny_indicate(struct ast_channel *ast, int ind, const void *data, s
                                transmit_callstateonly(d, sub, SKINNY_RINGOUT);
                                transmit_dialednumber(d, l->lastnumberdialed, l->instance, sub->callid);
                                transmit_displaypromptstatus(d, "Ring Out", 0, l->instance, sub->callid);
-                               transmit_callinfo(d, ast->cid.cid_name, ast->cid.cid_num, l->lastnumberdialed, l->lastnumberdialed, l->instance, sub->callid, 2); /* 2 = outgoing from phone */
+                               transmit_callinfo(d, ast->cid.cid_name, ast->cid.cid_num, S_OR(ast->connected.id.name, l->lastnumberdialed), S_OR(ast->connected.id.number, l->lastnumberdialed), l->instance, sub->callid, 2); /* 2 = outgoing from phone */
                                sub->ringing = 1;
                                if (!d->earlyrtp) {
                                        break;
@@ -3881,7 +4326,7 @@ static int skinny_indicate(struct ast_channel *ast, int ind, const void *data, s
                        }
                        transmit_callstateonly(d, sub, SKINNY_PROGRESS);
                        transmit_displaypromptstatus(d, "Call Progress", 0, l->instance, sub->callid);
-                       transmit_callinfo(d, ast->cid.cid_name, ast->cid.cid_num, l->lastnumberdialed, l->lastnumberdialed, l->instance, sub->callid, 2); /* 2 = outgoing from phone */
+                       transmit_callinfo(d, ast->cid.cid_name, ast->cid.cid_num, S_OR(ast->connected.id.name, l->lastnumberdialed), S_OR(ast->connected.id.number, l->lastnumberdialed), l->instance, sub->callid, 2); /* 2 = outgoing from phone */
                        sub->progress = 1;
                        if (!d->earlyrtp) {
                                break;
@@ -3900,7 +4345,10 @@ static int skinny_indicate(struct ast_channel *ast, int ind, const void *data, s
        case AST_CONTROL_PROCEEDING:
                break;
        case AST_CONTROL_SRCUPDATE:
-               ast_rtp_new_source(sub->rtp);
+               ast_rtp_instance_new_source(sub->rtp);
+               break;
+       case AST_CONTROL_CONNECTED_LINE:
+               update_connectedline(sub, data, datalen);
                break;
        default:
                ast_log(LOG_WARNING, "Don't know how to indicate condition %d\n", ind);
@@ -3909,7 +4357,7 @@ static int skinny_indicate(struct ast_channel *ast, int ind, const void *data, s
        return 0;
 }
 
-static struct ast_channel *skinny_new(struct skinny_line *l, int state)
+static struct ast_channel *skinny_new(struct skinny_line *l, int state, const char *linkedid)
 {
        struct ast_channel *tmp;
        struct skinny_subchannel *sub;
@@ -3922,7 +4370,7 @@ static struct ast_channel *skinny_new(struct skinny_line *l, int state)
                return NULL;
        }
 
-       tmp = ast_channel_alloc(1, state, l->cid_num, l->cid_name, l->accountcode, l->exten, l->context, l->amaflags, "Skinny/%s@%s-%d", l->name, d->name, callnums);
+       tmp = ast_channel_alloc(1, state, l->cid_num, l->cid_name, l->accountcode, l->exten, l->context, linkedid, l->amaflags, "Skinny/%s@%s-%d", l->name, d->name, callnums);
        if (!tmp) {
                ast_log(LOG_WARNING, "Unable to allocate channel structure\n");
                return NULL;
@@ -3956,10 +4404,14 @@ static struct ast_channel *skinny_new(struct skinny_line *l, int state)
                        // Should throw an error
                        tmp->nativeformats = default_capability;
                fmt = ast_best_codec(tmp->nativeformats);
-               if (skinnydebug)
-                       ast_verb(1, "skinny_new: tmp->nativeformats=%d fmt=%d\n", tmp->nativeformats, fmt);
+               if (skinnydebug) {
+                       char buf[256];
+                       ast_verb(1, "skinny_new: tmp->nativeformats=%s fmt=%s\n",
+                               ast_getformatname_multiple(buf, sizeof(buf), tmp->nativeformats),
+                               ast_getformatname(fmt));
+               }
                if (sub->rtp) {
-                       ast_channel_set_fd(tmp, 0, ast_rtp_fd(sub->rtp));
+                       ast_channel_set_fd(tmp, 0, ast_rtp_instance_fd(sub->rtp, 0));
                }
                if (state == AST_STATE_RING) {
                        tmp->rings = 1;
@@ -3972,6 +4424,8 @@ static struct ast_channel *skinny_new(struct skinny_line *l, int state)
                        ast_string_field_set(tmp, language, l->language);
                if (!ast_strlen_zero(l->accountcode))
                        ast_string_field_set(tmp, accountcode, l->accountcode);
+               if (!ast_strlen_zero(l->parkinglot))
+                       ast_string_field_set(tmp, parkinglot, l->parkinglot);
                if (l->amaflags)
                        tmp->amaflags = l->amaflags;
 
@@ -4105,7 +4559,7 @@ static int handle_transfer_button(struct skinny_subchannel *sub)
                if (!sub->onhold) {
                        skinny_hold(sub);
                }
-               c = skinny_new(l, AST_STATE_DOWN);
+               c = skinny_new(l, AST_STATE_DOWN, NULL);
                if (c) {
                        newsub = c->tech_pvt;
                        /* point the sub and newsub at each other so we know they are related */
@@ -4155,7 +4609,6 @@ static int handle_keep_alive_message(struct skinny_req *req, struct skinnysessio
                return -1;
 
        transmit_response(s->device, req);
-       do_housekeeping(s);
        return 1;
 }
 
@@ -4301,7 +4754,7 @@ static int handle_keypad_button_message(struct skinny_req *req, struct skinnyses
                ast_log(LOG_WARNING, "Unsupported digit %d\n", digit);
        }
 
-       f.subclass = dgt;
+       f.subclass.integer = dgt;
 
        f.src = "skinny";
 
@@ -4384,7 +4837,7 @@ static int handle_stimulus_message(struct skinny_req *req, struct skinnysession
                        break;
                }
 
-               c = skinny_new(l, AST_STATE_DOWN);
+               c = skinny_new(l, AST_STATE_DOWN, NULL);
                if (!c) {
                        ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
                } else {
@@ -4422,7 +4875,7 @@ static int handle_stimulus_message(struct skinny_req *req, struct skinnysession
                }
 
                if (!sub || !sub->owner)
-                       c = skinny_new(l, AST_STATE_DOWN);
+                       c = skinny_new(l, AST_STATE_DOWN, NULL);
                else
                        c = sub->owner;
 
@@ -4482,7 +4935,7 @@ static int handle_stimulus_message(struct skinny_req *req, struct skinnysession
                        ast_verb(1, "Received Stimulus: Voicemail(%d/%d)\n", instance, callreference);
 
                if (!sub || !sub->owner) {
-                       c = skinny_new(l, AST_STATE_DOWN);
+                       c = skinny_new(l, AST_STATE_DOWN, NULL);
                } else {
                        c = sub->owner;
                }
@@ -4567,7 +5020,7 @@ static int handle_stimulus_message(struct skinny_req *req, struct skinnysession
                        ast_verb(1, "Received Stimulus: Forward All(%d/%d)\n", instance, callreference);
 
                if (!sub || !sub->owner) {
-                       c = skinny_new(l, AST_STATE_DOWN);
+                       c = skinny_new(l, AST_STATE_DOWN, NULL);
                } else {
                        c = sub->owner;
                }
@@ -4584,7 +5037,7 @@ static int handle_stimulus_message(struct skinny_req *req, struct skinnysession
                        ast_verb(1, "Received Stimulus: Forward Busy (%d/%d)\n", instance, callreference);
 
                if (!sub || !sub->owner) {
-                       c = skinny_new(l, AST_STATE_DOWN);
+                       c = skinny_new(l, AST_STATE_DOWN, NULL);
                } else {
                        c = sub->owner;
                }
@@ -4602,7 +5055,7 @@ static int handle_stimulus_message(struct skinny_req *req, struct skinnysession
 
 #if 0 /* Not sure how to handle this yet */
                if (!sub || !sub->owner) {
-                       c = skinny_new(l, AST_STATE_DOWN);
+                       c = skinny_new(l, AST_STATE_DOWN, NULL);
                } else {
                        c = sub->owner;
                }
@@ -4653,7 +5106,7 @@ static int handle_stimulus_message(struct skinny_req *req, struct skinnysession
                        if (sub && sub->owner) {
                                ast_debug(1, "Current subchannel [%s] already has owner\n", sub->owner->name);
                        } else {
-                               c = skinny_new(l, AST_STATE_DOWN);
+                               c = skinny_new(l, AST_STATE_DOWN, NULL);
                                if (c) {
                                        sub = c->tech_pvt;
                                        l->activesub = sub;
@@ -4751,7 +5204,7 @@ static int handle_offhook_message(struct skinny_req *req, struct skinnysession *
                if (sub && sub->owner) {
                        ast_debug(1, "Current sub [%s] already has owner\n", sub->owner->name);
                } else {
-                       c = skinny_new(l, AST_STATE_DOWN);
+                       c = skinny_new(l, AST_STATE_DOWN, NULL);
                        if (c) {
                                sub = c->tech_pvt;
                                l->activesub = sub;
@@ -4846,10 +5299,6 @@ static int handle_onhook_message(struct skinny_req *req, struct skinnysession *s
                                l->name, d->name, sub->callid);
                }
        }
-       /* The bit commented below gives a very occasional core dump. */
-       if ((l->hookstate == SKINNY_ONHOOK) && (AST_LIST_NEXT(sub, list) /*&& !AST_LIST_NEXT(sub, list)->rtp*/)) {
-               do_housekeeping(s);
-       }
        return 1;
 }
 
@@ -4858,8 +5307,9 @@ static int handle_capabilities_res_message(struct skinny_req *req, struct skinny
        struct skinny_device *d = s->device;
        struct skinny_line *l;
        uint32_t count = 0;
-       int codecs = 0;
+       format_t codecs = 0;
        int i;
+       char buf[256];
 
        count = letohl(req->data.caps.count);
        if (count > SKINNY_MAX_CAPABILITIES) {
@@ -4868,17 +5318,17 @@ static int handle_capabilities_res_message(struct skinny_req *req, struct skinny
        }
 
        for (i = 0; i < count; i++) {
-               int acodec = 0;
+               format_t acodec = 0;
                int scodec = 0;
                scodec = letohl(req->data.caps.caps[i].codec);
                acodec = codec_skinny2ast(scodec);
                if (skinnydebug)
-                       ast_verb(1, "Adding codec capability '%d (%d)'\n", acodec, scodec);
+                       ast_verb(1, "Adding codec capability '%" PRId64 " (%d)'\n", acodec, scodec);
                codecs |= acodec;
        }
 
        d->capability = d->confcapability & codecs;
-       ast_verb(0, "Device capability set to '%d'\n", d->capability);
+       ast_verb(0, "Device capability set to '%s'\n", ast_getformatname_multiple(buf, sizeof(buf), d->capability));
        AST_LIST_TRAVERSE(&d->lines, l, list) {
                ast_mutex_lock(&l->lock);
                l->capability = l->confcapability & d->capability;
@@ -5161,8 +5611,8 @@ static int handle_open_receive_channel_ack_message(struct skinny_req *req, struc
        struct skinny_line *l;
        struct skinny_subchannel *sub;
        struct ast_format_list fmt;
-       struct sockaddr_in sin;
-       struct sockaddr_in us;
+       struct sockaddr_in sin = { 0, };
+       struct sockaddr_in us = { 0, };
        uint32_t addr;
        int port;
        int status;
@@ -5189,8 +5639,8 @@ static int handle_open_receive_channel_ack_message(struct skinny_req *req, struc
        l = sub->parent;
 
        if (sub->rtp) {
-               ast_rtp_set_peer(sub->rtp, &sin);
-               ast_rtp_get_us(sub->rtp, &us);
+               ast_rtp_instance_set_remote_address(sub->rtp, &sin);
+               ast_rtp_instance_get_local_address(sub->rtp, &us);
        } else {
                ast_log(LOG_ERROR, "No RTP structure, this is very bad\n");
                return 0;
@@ -5205,7 +5655,7 @@ static int handle_open_receive_channel_ack_message(struct skinny_req *req, struc
        fmt = ast_codec_pref_getsize(&l->prefs, ast_best_codec(l->capability));
 
        if (skinnydebug)
-               ast_verb(1, "Setting payloadType to '%d' (%d ms)\n", fmt.bits, fmt.cur_ms);
+               ast_verb(1, "Setting payloadType to '%s' (%d ms)\n", ast_getformatname(fmt.bits), fmt.cur_ms);
 
        req->data.startmedia.conferenceId = htolel(sub->callid);
        req->data.startmedia.passThruPartyId = htolel(sub->callid);
@@ -5244,7 +5694,7 @@ static int handle_enbloc_call_message(struct skinny_req *req, struct skinnysessi
                l = sub->parent;
        }
 
-       c = skinny_new(l, AST_STATE_DOWN);
+       c = skinny_new(l, AST_STATE_DOWN, NULL);
 
        if(!c) {
                ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
@@ -5358,7 +5808,7 @@ static int handle_soft_key_event_message(struct skinny_req *req, struct skinnyse
                }
 
                if (!sub || !sub->owner) {
-                       c = skinny_new(l, AST_STATE_DOWN);
+                       c = skinny_new(l, AST_STATE_DOWN, NULL);
                } else {
                        c = sub->owner;
                }
@@ -5394,7 +5844,7 @@ static int handle_soft_key_event_message(struct skinny_req *req, struct skinnyse
                        ast_verb(1, "Received Softkey Event: New Call(%d/%d)\n", instance, callreference);
 
                /* New Call ALWAYS gets a new sub-channel */
-               c = skinny_new(l, AST_STATE_DOWN);
+               c = skinny_new(l, AST_STATE_DOWN, NULL);
                sub = c->tech_pvt;
        
                /* transmit_ringer_mode(d, SKINNY_RING_OFF);
@@ -5464,7 +5914,7 @@ static int handle_soft_key_event_message(struct skinny_req *req, struct skinnyse
                        ast_verb(1, "Received Softkey Event: Forward All(%d/%d)\n", instance, callreference);
 
                if (!sub || !sub->owner) {
-                       c = skinny_new(l, AST_STATE_DOWN);
+                       c = skinny_new(l, AST_STATE_DOWN, NULL);
                } else {
                        c = sub->owner;
                }
@@ -5482,7 +5932,7 @@ static int handle_soft_key_event_message(struct skinny_req *req, struct skinnyse
                        ast_verb(1, "Received Softkey Event: Forward Busy (%d/%d)\n", instance, callreference);
 
                if (!sub || !sub->owner) {
-                       c = skinny_new(l, AST_STATE_DOWN);
+                       c = skinny_new(l, AST_STATE_DOWN, NULL);
                } else {
                        c = sub->owner;
                }
@@ -5501,7 +5951,7 @@ static int handle_soft_key_event_message(struct skinny_req *req, struct skinnyse
 
 #if 0 /* Not sure how to handle this yet */
                if (!sub || !sub->owner) {
-                       c = skinny_new(l, AST_STATE_DOWN);
+                       c = skinny_new(l, AST_STATE_DOWN, NULL);
                } else {
                        c = sub->owner;
                }
@@ -5568,7 +6018,6 @@ static int handle_soft_key_event_message(struct skinny_req *req, struct skinnyse
                                }
                        }
                        if ((l->hookstate == SKINNY_ONHOOK) && (AST_LIST_NEXT(sub, list) && !AST_LIST_NEXT(sub, list)->rtp)) {
-                               do_housekeeping(s);
                                ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Skinny/%s@%s", l->name, d->name);
                        }
                }
@@ -5875,9 +6324,9 @@ static void destroy_session(struct skinnysession *s)
 {
        struct skinnysession *cur;
        AST_LIST_LOCK(&sessions);
-       AST_LIST_TRAVERSE(&sessions, cur, list) {
+       AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions, cur, list) {
                if (cur == s) {
-                       AST_LIST_REMOVE(&sessions, s, list);
+                       AST_LIST_REMOVE_CURRENT(list);
                        if (s->fd > -1) 
                                close(s->fd);
                        
@@ -5888,6 +6337,7 @@ static void destroy_session(struct skinnysession *s)
                        ast_log(LOG_WARNING, "Trying to delete nonexistent session %p?\n", s);
                }
        }
+       AST_LIST_TRAVERSE_SAFE_END
        AST_LIST_UNLOCK(&sessions);
 }
 
@@ -5895,12 +6345,13 @@ static int get_input(struct skinnysession *s)
 {
        int res;
        int dlen = 0;
+       int *bufaddr;
        struct pollfd fds[1];
 
        fds[0].fd = s->fd;
        fds[0].events = POLLIN;
        fds[0].revents = 0;
-       res = poll(fds, 1, (keep_alive * 1100)); /* If nothing has happen, client is dead */
+       res = ast_poll(fds, 1, (keep_alive * 1100)); /* If nothing has happen, client is dead */
                                                 /* we add 10% to the keep_alive to deal */
                                                 /* with network delays, etc */
        if (res < 0) {
@@ -5941,7 +6392,8 @@ static int get_input(struct skinnysession *s)
                        return -1;
                }
 
-               dlen = letohl(*(int *)s->inbuf);
+               bufaddr = (int *)s->inbuf;
+               dlen = letohl(*bufaddr);
                if (dlen < 4) {
                        ast_debug(1, "Skinny Client sent invalid data.\n");
                        ast_mutex_unlock(&s->lock);
@@ -5950,7 +6402,7 @@ static int get_input(struct skinnysession *s)
                if (dlen+8 > sizeof(s->inbuf)) {
                        dlen = sizeof(s->inbuf) - 8;
                }
-               *(int *)s->inbuf = htolel(dlen);
+               *bufaddr = htolel(dlen);
 
                res = read(s->fd, s->inbuf+4, dlen+4);
                ast_mutex_unlock(&s->lock);
@@ -5969,13 +6421,15 @@ static int get_input(struct skinnysession *s)
 static struct skinny_req *skinny_req_parse(struct skinnysession *s)
 {
        struct skinny_req *req;
+       int *bufaddr;
 
        if (!(req = ast_calloc(1, SKINNY_MAX_PACKET)))
                return NULL;
 
        ast_mutex_lock(&s->lock);
        memcpy(req, s->inbuf, skinny_header_size);
-       memcpy(&req->data, s->inbuf+skinny_header_size, letohl(*(int*)(s->inbuf))-4);
+       bufaddr = (int *)(s->inbuf);
+       memcpy(&req->data, s->inbuf+skinny_header_size, letohl(*bufaddr)-4);
 
        ast_mutex_unlock(&s->lock);
 
@@ -6131,9 +6585,9 @@ static int skinny_devicestate(void *data)
        return get_devicestate(l);
 }
 
-static struct ast_channel *skinny_request(const char *type, int format, void *data, int *cause)
+static struct ast_channel *skinny_request(const char *type, format_t format, const struct ast_channel *requestor, void *data, int *cause)
 {
-       int oldformat;
+       format_t oldformat;
        
        struct skinny_line *l;
        struct ast_channel *tmpc = NULL;
@@ -6143,7 +6597,7 @@ static struct ast_channel *skinny_request(const char *type, int format, void *da
        oldformat = format;
        
        if (!(format &= AST_FORMAT_AUDIO_MASK)) {
-               ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%d'\n", format);
+               ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%s'\n", ast_getformatname_multiple(tmp, sizeof(tmp), format));
                return NULL;
        }
 
@@ -6158,7 +6612,7 @@ static struct ast_channel *skinny_request(const char *type, int format, void *da
                return NULL;
        }
        ast_verb(3, "skinny_request(%s)\n", tmp);
-       tmpc = skinny_new(l, AST_STATE_DOWN);
+       tmpc = skinny_new(l, AST_STATE_DOWN, requestor ? requestor->linkedid : NULL);
        if (!tmpc) {
                ast_log(LOG_WARNING, "Unable to make channel for '%s'\n", tmp);
        }
@@ -6248,7 +6702,7 @@ static struct ast_channel *skinny_request(const char *type, int format, void *da
                                        ast_log(LOG_WARNING, "Invalid cos_video value at line %d, refer to QoS documentation\n", v->lineno);
                                continue;
                        } else if (!strcasecmp(v->name, "bindport")) {
-                               if (sscanf(v->value, "%d", &ourport) == 1) {
+                               if (sscanf(v->value, "%5d", &ourport) == 1) {
                                        bindaddr.sin_port = htons(ourport);
                                } else {
                                        ast_log(LOG_WARNING, "Invalid bindport '%s' at line %d of %s\n", v->value, v->lineno, config);
@@ -6279,9 +6733,9 @@ static struct ast_channel *skinny_request(const char *type, int format, void *da
                                CLINE_OPTS->callwaiting = ast_true(v->value);
                                continue;
                        }
-               } else if (!strcasecmp(v->name, "canreinvite")) {
+               } else if (!strcasecmp(v->name, "directmedia") || !strcasecmp(v->name, "canreinvite")) {
                        if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
-                               CLINE_OPTS->canreinvite = ast_true(v->value);
+                               CLINE_OPTS->directmedia = ast_true(v->value);
                                continue;
                        }
                } else if (!strcasecmp(v->name, "nat")) {
@@ -6466,7 +6920,7 @@ static struct ast_channel *skinny_request(const char *type, int format, void *da
                        if (type & (TYPE_DEVICE)) {
                                struct skinny_line *l;
                                AST_LIST_TRAVERSE(&lines, l, all) {
-                                       if (!strcasecmp(v->value, l->name)) {
+                                       if (!strcasecmp(v->value, l->name) && !l->prune) {
 
                                                /* FIXME: temp solution about line conflicts */
                                                struct skinny_device *d;
@@ -6474,7 +6928,7 @@ static struct ast_channel *skinny_request(const char *type, int format, void *da
                                                int lineinuse = 0;
                                                AST_LIST_TRAVERSE(&devices, d, list) {
                                                        AST_LIST_TRAVERSE(&d->lines, l2, list) {
-                                                               if (l2 == l) {
+                                                               if (l2 == l && strcasecmp(d->id, CDEV->id)) {
                                                                        ast_log(LOG_WARNING, "Line %s already used by %s. Not connecting to %s.\n", l->name, d->name, CDEV->name);
                                                                        lineinuse++;
                                                                }
@@ -6548,32 +7002,35 @@ static struct ast_channel *skinny_request(const char *type, int format, void *da
  
  static struct skinny_line *config_line(const char *lname, struct ast_variable *v)
  {
-       struct skinny_line *l;
+       struct skinny_line *l, *temp;
+       int update = 0;
  
        ast_log(LOG_NOTICE, "Configuring skinny line %s.\n", lname);
-       
+
+       /* We find the old line and remove it just before the new
+          line is created */
        AST_LIST_LOCK(&lines);
-       AST_LIST_TRAVERSE(&lines, l, all) {
-               if (!strcasecmp(lname, l->name)) {
-                       ast_log(LOG_NOTICE, "Line %s already exists. Reconfiguring.\n", lname);
+       AST_LIST_TRAVERSE(&lines, temp, all) {
+               if (!strcasecmp(lname, temp->name) && temp->prune) {
+                       update = 1;
                        break;
                }
        }
-       if (!l) {
-               ast_log(LOG_NOTICE, "Creating line %s.\n", lname);
-               if (!(l=ast_calloc(1, sizeof(*l)))) {
-                       ast_verb(1, "Unable to allocate memory for line %s.\n", lname);
-                       AST_LIST_UNLOCK(&lines);
-                       return NULL;
-               }
-               memcpy(l, default_line, sizeof(*default_line));
-               ast_mutex_init(&l->lock);
-               ast_copy_string(l->name, lname, sizeof(l->name));
-               AST_LIST_INSERT_TAIL(&lines, l, all);
+
+       if (!(l=ast_calloc(1, sizeof(*l)))) {
+               ast_verb(1, "Unable to allocate memory for line %s.\n", lname);
+               AST_LIST_UNLOCK(&lines);
+               return NULL;
        }
+
+       memcpy(l, default_line, sizeof(*default_line));
+       ast_mutex_init(&l->lock);
+       ast_copy_string(l->name, lname, sizeof(l->name));
+       AST_LIST_INSERT_TAIL(&lines, l, all);
+
        ast_mutex_lock(&l->lock);
        AST_LIST_UNLOCK(&lines);
+
        config_parse_variables(TYPE_LINE, l, v);
                        
        if (!ast_strlen_zero(l->mailbox)) {
@@ -6583,7 +7040,7 @@ static struct ast_channel *skinny_request(const char *type, int format, void *da
                strsep(&cfg_context, "@");
                if (ast_strlen_zero(cfg_context))
                         cfg_context = "default";
-               l->mwi_event_sub = ast_event_subscribe(AST_EVENT_MWI, mwi_event_cb, l,
+               l->mwi_event_sub = ast_event_subscribe(AST_EVENT_MWI, mwi_event_cb, "skinny MWI subsciption", l,
                        AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, cfg_mailbox,
                        AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, cfg_context,
                        AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_EXISTS,
@@ -6591,32 +7048,43 @@ static struct ast_channel *skinny_request(const char *type, int format, void *da
        }
  
        ast_mutex_unlock(&l->lock);
+       
+       /* We do not want to unlink or free the line yet, it needs
+          to be available to detect a device reconfig when we load the
+          devices.  Old lines will be pruned after the reload completes */
+
+       ast_verb(3, "%s config for line '%s'\n", update ? "Updated" : (skinnyreload ? "Reloaded" : "Created"), l->name);
+
        return l;
  }
  
  static struct skinny_device *config_device(const char *dname, struct ast_variable *v)
  {
-       struct skinny_device *d;
+       struct skinny_device *d, *temp;
+       struct skinny_line *l, *ltemp;
+       struct skinny_subchannel *sub;
+       int update = 0;
  
        ast_log(LOG_NOTICE, "Configuring skinny device %s.\n", dname);
-       
+
        AST_LIST_LOCK(&devices);
-       AST_LIST_TRAVERSE(&devices, d, list) {
-               if (!strcasecmp(dname, d->name)) {
+       AST_LIST_TRAVERSE(&devices, temp, list) {
+               if (!strcasecmp(dname, temp->name) && temp->prune) {
+                       update = 1;
                        break;
                }
        }
-       if (!d) {
-               if (!(d = ast_calloc(1, sizeof(*d)))) {
-                       ast_verb(1, "Unable to allocate memory for device %s.\n", dname);
-                       AST_LIST_UNLOCK(&devices);
-                       return NULL;
-               }
-               memcpy(d, default_device, sizeof(*default_device));
-               ast_mutex_init(&d->lock);
-               ast_copy_string(d->name, dname, sizeof(d->name));
-               AST_LIST_INSERT_HEAD(&devices, d, list);
+
+       if (!(d = ast_calloc(1, sizeof(*d)))) {
+               ast_verb(1, "Unable to allocate memory for device %s.\n", dname);
+               AST_LIST_UNLOCK(&devices);
+               return NULL;
        }
+       memcpy(d, default_device, sizeof(*default_device));
+       ast_mutex_init(&d->lock);
+       ast_copy_string(d->name, dname, sizeof(d->name));
+       AST_LIST_INSERT_TAIL(&devices, d, list);
+
        ast_mutex_lock(&d->lock);
        AST_LIST_UNLOCK(&devices);
  
@@ -6631,8 +7099,52 @@ static struct ast_channel *skinny_request(const char *type, int format, void *da
                d->addr.sin_port = htons(DEFAULT_SKINNY_PORT);
        }
  
+       if (skinnyreload){
+               AST_LIST_LOCK(&devices);
+               AST_LIST_TRAVERSE(&devices, temp, list) {
+                       if (strcasecmp(d->id, temp->id) || !temp->prune || !temp->session) {
+                               continue;
+                       }
+                       ast_mutex_lock(&d->lock);
+                       d->session = temp->session;
+                       d->session->device = d;
+
+                       AST_LIST_LOCK(&d->lines);
+                       AST_LIST_TRAVERSE(&d->lines, l, list){
+                               l->device = d;  
+
+                               AST_LIST_LOCK(&temp->lines);
+                               AST_LIST_TRAVERSE(&temp->lines, ltemp, list) {
+                                       if (strcasecmp(l->name, ltemp->name)) {
+                                               continue;
+                                       }
+                                       ast_mutex_lock(&ltemp->lock);
+                                       l->instance = ltemp->instance;
+                                       l->hookstate = ltemp->hookstate;
+                                       if (!AST_LIST_EMPTY(&ltemp->sub)) {
+                                               ast_mutex_lock(&l->lock);
+                                               l->sub = ltemp->sub;
+                                               AST_LIST_TRAVERSE(&l->sub, sub, list) {
+                                                       sub->parent = l;
+                                               }
+                                               ast_mutex_unlock(&l->lock);
+                                       }
+                                       ast_mutex_unlock(&ltemp->lock);
+                               }
+                               AST_LIST_UNLOCK(&temp->lines);
+                       }
+                       AST_LIST_UNLOCK(&d->lines);
+                       ast_mutex_unlock(&d->lock);
+               }
+               AST_LIST_UNLOCK(&devices);
+       }
+
        ast_mutex_unlock(&d->lock);
-       return d;
+
+       ast_verb(3, "%s config for device '%s'\n", update ? "Updated" : (skinnyreload ? "Reloaded" : "Created"), d->name);
+       
+       return d;
+
  }
  
  static int config_load(void)
@@ -6667,7 +7179,7 @@ static struct ast_channel *skinny_request(const char *type, int format, void *da
        /* load the general section */
        cat = ast_category_browse(cfg, "general");
        config_parse_variables(TYPE_GENERAL, NULL, ast_variable_browse(cfg, "general"));
-               
+
        if (ntohl(bindaddr.sin_addr.s_addr)) {
                __ourip = bindaddr.sin_addr;
        } else {
@@ -6782,19 +7294,87 @@ static void delete_devices(void)
        AST_LIST_UNLOCK(&devices);
 }
 
-#if 0
-/*
- * XXX This never worked properly anyways.
- * Let's get rid of it, until we can fix it.
- */
-static int reload(void)
+int skinny_reload(void)
 {
-       delete_devices();
-       config_load();
-       restart_monitor();
-       return 0;
+       struct skinny_device *d;
+       struct skinny_line *l;
+       struct skinny_speeddial *sd;
+       struct skinny_addon *a;
+       struct skinny_req *req;
+
+       if (skinnyreload) {
+               ast_verb(3, "Chan_skinny is already reloading.\n");
+               return 0;
+       }
+
+       skinnyreload = 1;
+
+       /* Mark all devices and lines as candidates to be pruned */
+       AST_LIST_LOCK(&devices);
+       AST_LIST_TRAVERSE(&devices, d, list) {
+               d->prune = 1;
+       }
+       AST_LIST_UNLOCK(&devices);
+
+       AST_LIST_LOCK(&lines);
+       AST_LIST_TRAVERSE(&lines, l, all) {
+               l->prune = 1;
+       }
+       AST_LIST_UNLOCK(&lines);
+
+        config_load();
+
+       /* Remove any devices that no longer exist in the config */
+       AST_LIST_LOCK(&devices);
+       AST_LIST_TRAVERSE_SAFE_BEGIN(&devices, d, list) {
+               if (!d->prune) {
+                       continue;
+               }
+               ast_verb(3, "Removing device '%s'\n", d->name);
+               /* Delete all lines for this device. 
+                  We do not want to free the line here, that
+                  will happen below. */
+               while ((l = AST_LIST_REMOVE_HEAD(&d->lines, list))) {
+               }
+               /* Delete all speeddials for this device */
+               while ((sd = AST_LIST_REMOVE_HEAD(&d->speeddials, list))) {
+                       free(sd);
+               }
+               /* Delete all addons for this device */
+               while ((a = AST_LIST_REMOVE_HEAD(&d->addons, list))) {
+                       free(a);
+               }
+               AST_LIST_REMOVE_CURRENT(list);
+               free(d);
+       }
+       AST_LIST_TRAVERSE_SAFE_END;
+       AST_LIST_UNLOCK(&devices);
+
+       AST_LIST_LOCK(&lines);  
+       AST_LIST_TRAVERSE_SAFE_BEGIN(&lines, l, all) {
+               if (l->prune) {
+                       AST_LIST_REMOVE_CURRENT(all);
+                       free(l);
+               }
+       }
+       AST_LIST_TRAVERSE_SAFE_END;
+       AST_LIST_UNLOCK(&lines);  
+
+       AST_LIST_TRAVERSE(&devices, d, list) {
+               /* Do a soft reset to re-register the devices after
+                  cleaning up the removed devices and lines */
+               if (d->session) {
+                       ast_verb(3, "Restarting device '%s'\n", d->name);
+                       if ((req = req_alloc(sizeof(struct reset_message), RESET_MESSAGE))) {
+                               req->data.reset.resetType = 2;
+                               transmit_response(d, req);
+                       }
+               }
+       }
+       
+       skinnyreload = 0;
+        return 0;
 }
-#endif
 
 static int load_module(void)
 {
@@ -6815,8 +7395,14 @@ static int load_module(void)
                return -1;
        }
 
-       ast_rtp_proto_register(&skinny_rtp);
+       ast_rtp_glue_register(&skinny_rtp_glue);
        ast_cli_register_multiple(cli_skinny, ARRAY_LEN(cli_skinny));
+
+       ast_manager_register_xml("SKINNYdevices", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_skinny_show_devices);
+       ast_manager_register_xml("SKINNYshowdevice", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_skinny_show_device);
+       ast_manager_register_xml("SKINNYlines", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_skinny_show_lines);
+       ast_manager_register_xml("SKINNYshowline", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_skinny_show_line);
+
        sched = sched_context_create();
        if (!sched) {
                ast_log(LOG_WARNING, "Unable to create schedule context\n");
@@ -6828,7 +7414,7 @@ static int load_module(void)
        /* And start the monitor for the first time */
        restart_monitor();
 
-       return res;
+       return AST_MODULE_LOAD_SUCCESS;
 }
 
 static int unload_module(void)
@@ -6839,9 +7425,14 @@ static int unload_module(void)
        struct skinny_subchannel *sub;
        struct ast_context *con;
 
-       ast_rtp_proto_unregister(&skinny_rtp);
+       ast_rtp_glue_unregister(&skinny_rtp_glue);
        ast_channel_unregister(&skinny_tech);
        ast_cli_unregister_multiple(cli_skinny, ARRAY_LEN(cli_skinny));
+
+       ast_manager_unregister("SKINNYdevices");
+       ast_manager_unregister("SKINNYshowdevice");
+       ast_manager_unregister("SKINNYlines");
+       ast_manager_unregister("SKINNYshowline");
        
        AST_LIST_LOCK(&sessions);
        /* Destroy all the interfaces and free their memory */
@@ -6860,6 +7451,7 @@ static int unload_module(void)
                        if (l->mwi_event_sub)
                                ast_event_unsubscribe(l->mwi_event_sub);
                        ast_mutex_unlock(&l->lock);
+                       manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: Skinny\r\nPeer: Skinny/%s@%s\r\nPeerStatus: Unregistered\r\n", l->name, d->name);
                        unregister_exten(l);
                }
                if (s->fd > -1)
@@ -6902,7 +7494,14 @@ static int unload_module(void)
        return 0;
 }
 
+static int reload(void)
+{
+       skinny_reload();
+       return 0;
+}
+
 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Skinny Client Control Protocol (Skinny)",
                .load = load_module,
                .unload = unload_module,
+               .reload = reload,
 );