Index: chan_unistim.c
===================================================================
--- chan_unistim.c	(revision 293272)
+++ chan_unistim.c	(working copy)
@@ -32,12 +32,20 @@
  * \ingroup channel_drivers
  */
 
+/*
+
+ TODO: Fix playback of pickupfile in gsm format when bridged channel in alaw, that cause
+ * chan->writetrans to be empty when gsm frames played
+ 
+ */
+
 #include "asterisk.h"
 
 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <sys/stat.h>
 #include <signal.h>
+#include <iconv.h>
 
 #if defined(__CYGWIN__)
 /*
@@ -69,6 +77,8 @@
 #include "asterisk/musiconhold.h"
 #include "asterisk/causes.h"
 #include "asterisk/indications.h"
+#include "asterisk/features.h"
+#include "asterisk/astobj2.h"
 
 /*! Beware, G729 and G723 are not supported by asterisk, except with the proper licence */
 #define CAPABILITY AST_FORMAT_ALAW | AST_FORMAT_ULAW    /* | AST_FORMAT_G729A | AST_FORMAT_G723_1 */
@@ -78,11 +88,14 @@
 #define DEFAULTCALLERNAME       " "
 #define DEFAULTHEIGHT	 3
 #define USTM_LOG_DIR	    "unistimHistory"
+#define USTM_LANG_DIR	    "unistimLang"
 
 /*! Size of the transmit buffer */
 #define MAX_BUF_SIZE	    64
 /*! Number of slots for the transmit queue */
 #define MAX_BUF_NUMBER	  50
+/*! Number of digits displayed on screen */
+#define MAX_SCREEN_NUMBER   15
 /*! Try x times before removing the phone */
 #define NB_MAX_RETRANSMIT       8
 /*! Nb of milliseconds waited when no events are scheduled */
@@ -99,8 +112,9 @@
 #define MAX_ENTRY_LOG	   30
 
 #define SUB_REAL		0
-#define SUB_THREEWAY	    1
-#define MAX_SUBS		2
+#define SUB_THREEWAY            1
+#define SUB_ONHOLD              2
+#define SUB_RING                3
 
 enum autoprovision {
 	AUTOPROVISIONING_NO = 0,
@@ -181,6 +195,8 @@
 
 #define FAV_MAX_LENGTH		  0x0A
 
+#define FAVNUM                    6
+
 static void dummy(char *unused, ...)
 {
 	return;
@@ -208,6 +224,8 @@
 static enum autoprovision autoprovisioning = AUTOPROVISIONING_NO;
 static int unistim_keepalive;
 static int unistimsock = -1;
+static int key_sndhist = 0;
+static int key_rcvhist = 0;
 
 static struct {
 	unsigned int tos;
@@ -251,7 +269,10 @@
 	STATE_DIALPAGE,
 	STATE_RINGING,
 	STATE_CALL,
+        STATE_SELECTOPTION,
 	STATE_SELECTCODEC,
+        STATE_SELECTCONTRAST,
+        STATE_SELECTLANGUAGE,
 	STATE_CLEANING,
 	STATE_HISTORY
 };
@@ -302,46 +323,15 @@
 	KEY_INDEX = 0x7f
 };
 
-struct tone_zone_unistim {
-	char country[3];
-	int freq1;
-	int freq2;
+enum charset {
+        LANG_DEFAULT,
+        ISO_8859_1,
+        ISO_8859_2,
+        ISO_8859_4,
+        ISO_8859_5,
+        ISO_2022_JP,
 };
 
-static const struct tone_zone_unistim frequency[] = {
-	{"us", 350, 440},
-	{"fr", 440, 0},
-	{"au", 413, 438},
-	{"nl", 425, 0},
-	{"uk", 350, 440},
-	{"fi", 425, 0},
-	{"es", 425, 0},
-	{"jp", 400, 0},
-	{"no", 425, 0},
-	{"at", 420, 0},
-	{"nz", 400, 0},
-	{"tw", 350, 440},
-	{"cl", 400, 0},
-	{"se", 425, 0},
-	{"be", 425, 0},
-	{"sg", 425, 0},
-	{"il", 414, 0},
-	{"br", 425, 0},
-	{"hu", 425, 0},
-	{"lt", 425, 0},
-	{"pl", 425, 0},
-	{"za", 400, 0},
-	{"pt", 425, 0},
-	{"ee", 425, 0},
-	{"mx", 425, 0},
-	{"in", 400, 0},
-	{"de", 425, 0},
-	{"ch", 425, 0},
-	{"dk", 425, 0},
-	{"cn", 450, 0},
-	{"--", 0, 0}
-};
-
 struct wsabuf {
 	u_long len;
 	unsigned char *buf;
@@ -360,7 +350,7 @@
 
 struct unistim_subchannel {
 	ast_mutex_t lock;
-	/*! SUBS_REAL or SUBS_THREEWAY */
+	/*! SUB_REAL, SUB_THREEWAY or SUB_ONHOLD */
 	unsigned int subtype;
 	/*! Asterisk channel used by the subchannel */
 	struct ast_channel *owner;
@@ -368,9 +358,14 @@
 	struct unistim_line *parent;
 	/*! RTP handle */
 	struct ast_rtp_instance *rtp;
+        /*! Softkey assigned */
+        int softkey;
+	time_t start_call_timestamp;    /*!< timestamp for the length calculation of the call */
 	int alreadygone;
 	char ringvolume;
 	char ringstyle;
+	int moh;					/*!< Music on hold in progress */
+        AST_LIST_ENTRY(unistim_subchannel) list;
 };
 
 /*!
@@ -382,14 +377,10 @@
 	char name[80];
 	/*! Like USTM/200\@black */
 	char fullname[80];
-	/*! pointer to our current connection, channel... */
-	struct unistim_subchannel *subs[MAX_SUBS];
 	/*! Extension where to start */
 	char exten[AST_MAX_EXTENSION];
 	/*! Context to start in */
 	char context[AST_MAX_EXTENSION];
-	/*! Language for asterisk sounds */
-	char language[MAX_LANGUAGE];
 	/*! CallerID Number */
 	char cid_num[AST_MAX_EXTENSION];
 	/*! Mailbox for MWI */
@@ -414,6 +405,7 @@
 	char parkinglot[AST_MAX_CONTEXT];
 	struct unistim_line *next;
 	struct unistim_device *parent;
+        AST_LIST_ENTRY(unistim_line) list;
 };
 
 /*! 
@@ -422,19 +414,21 @@
 static struct unistim_device {
 	int receiver_state;	      /*!< state of the receiver (see ReceiverState) */
 	int size_phone_number;	  /*!< size of the phone number */
-	char phone_number[16];	  /*!< the phone number entered by the user */
-	char redial_number[16];	 /*!< the last phone number entered by the user */
+	char phone_number[AST_MAX_EXTENSION];	  /*!< the phone number entered by the user */
+	char redial_number[AST_MAX_EXTENSION];	 /*!< the last phone number entered by the user */
 	int phone_current;		      /*!< Number of the current phone */
 	int pos_fav;			    /*!< Position of the displayed favorites (used for scrolling) */
 	char id[18];			    /*!< mac address of the current phone in ascii */
 	char name[DEVICE_NAME_LEN];     /*!< name of the device */
-	int softkeylinepos;		     /*!< position of the line softkey (default 0) */
-	char softkeylabel[6][11];       /*!< soft key label */
-	char softkeynumber[6][16];      /*!< number dialed when the soft key is pressed */
-	char softkeyicon[6];	    /*!< icon number */
-	char softkeydevice[6][16];      /*!< name of the device monitored */
-	struct unistim_device *sp[6];   /*!< pointer to the device monitored by this soft key */
-	int height;							/*!< The number of lines the phone can display */
+	char softkeylabel[FAVNUM][11];       /*!< soft key label */
+	char softkeynumber[FAVNUM][AST_MAX_EXTENSION];      /*!< number dialed when the soft key is pressed */
+	char softkeyicon[FAVNUM];	    /*!< icon number */
+	char softkeydevice[FAVNUM][16];      /*!< name of the device monitored */
+        struct unistim_subchannel *ssub[FAVNUM];
+        struct unistim_line *sline[FAVNUM];
+	struct unistim_device *sp[FAVNUM];   /*!< pointer to the device monitored by this soft key */
+	char language[MAX_LANGUAGE];    /*!< Language for asterisk sounds */
+        int height;							/*!< The number of lines the phone can display */
 	char maintext0[25];		     /*!< when the phone is idle, display this string on line 0 */
 	char maintext1[25];		     /*!< when the phone is idle, display this string on line 1 */
 	char maintext2[25];		     /*!< when the phone is idle, display this string on line 2 */
@@ -451,6 +445,8 @@
 	char codec_number;		      /*!< The current codec used to make calls */
 	int missed_call;			/*!< Number of call unanswered */
 	int callhistory;			/*!< Allowed to record call history */
+        int selected;                           /*!< softkey selected */
+        int sharp_dial;				/*!< Execute Dial on '#' or not */
 	char lst_cid[TEXT_LENGTH_MAX];  /*!< Last callerID received */
 	char lst_cnm[TEXT_LENGTH_MAX];  /*!< Last callername recevied */
 	char call_forward[AST_MAX_EXTENSION];   /*!< Forward number */
@@ -458,15 +454,14 @@
 	int previous_output;	    /*!< Previous output */
 	int volume;				     /*!< Default volume */
 	int mute;				       /*!< Mute mode */
-	int moh;					/*!< Music on hold in progress */
 	int nat;					/*!< Used by the obscure ast_rtp_setnat */
 	enum autoprov_extn extension;   /*!< See ifdef EXTENSION for valid values */
 	char extension_number[11];      /*!< Extension number entered by the user */
 	char to_delete;			 /*!< Used in reload */
-	time_t start_call_timestamp;    /*!< timestamp for the length calculation of the call */
 	struct ast_silence_generator *silence_generator;
-	struct unistim_line *lines;
-	struct ast_ha *ha;
+	AST_LIST_HEAD(,unistim_subchannel) subs; /*!< pointer to our current connection, channel... */
+        AST_LIST_HEAD(,unistim_line) lines;
+        struct ast_ha *ha;
 	struct unistimsession *session;
 	struct unistim_device *next;
 } *devices = NULL;
@@ -489,9 +484,23 @@
 	struct wsabuf wsabufsend[MAX_BUF_NUMBER];      /*!< Size of each paquet stored in the buffer array & pointer to this buffer */
 	unsigned char buf[MAX_BUF_NUMBER][MAX_BUF_SIZE];	/*!< Buffer array used to keep the lastest non-acked paquets */
 	struct unistim_device *device;
-	struct unistimsession *next;
+        struct unistim_subchannel *active_sub;                  /*!< Active subchannel for detect active softkey */
+        struct unistimsession *next;
 } *sessions = NULL;
 
+struct unistim_menu_item {
+    char *label;
+    int state;
+    void (*handle_option)(struct unistimsession *);
+};
+
+struct unistim_languages {
+    char *label;
+    char *lang_short;
+    int encoding;
+    struct ao2_container *trans;
+};
+
 /*!
  * \page Unistim datagram formats
  *
@@ -623,7 +632,7 @@
 };
 static const unsigned char packet_send_Contrast[] =
 	{ 0x17, 0x04, 0x24, /*Contrast */ 0x08 };
-static const unsigned char packet_send_StartTimer[] =
+static const unsigned char packet_send_start_timer[] =
 	{ 0x17, 0x05, 0x0b, 0x05, 0x00, 0x17, 0x08, 0x16, /* Text */ 0x44, 0x75, 0x72, 0xe9,
 0x65 };
 static const unsigned char packet_send_stop_timer[] = { 0x17, 0x05, 0x0b, 0x02, 0x00 };
@@ -657,6 +666,23 @@
 	{ 0x17, 0x0b, 0x19, /* pos [08|28|48|68] */ 0x00, /* text */ 0x20, 0x20, 0x20, 0x20,
 0x20, 0x20, 0x20 /* end_text */  };
 
+/* Multiple character set support */
+/* ISO-8859-1 - Western European) */
+static const unsigned char packet_send_charset_iso_8859_1[] =  
+        { 0x17, 0x08, 0x21, 0x1b, 0x2d, 0x41, 0x1b, 0x00 };
+/* ISO-8859-2 - Central European) */
+static const unsigned char packet_send_charset_iso_8859_2[] =  
+        { 0x17, 0x08, 0x21, 0x1b, 0x2d, 0x42, 0x1b, 0x00 };
+/* ISO-8859-4 - Baltic) */
+static const unsigned char packet_send_charset_iso_8859_4[] =  
+        { 0x17, 0x08, 0x21, 0x1b, 0x2d, 0x44, 0x1b, 0x00 };
+/* ISO 8859-5 - cyrilic */
+static const unsigned char packet_send_charset_iso_8859_5[] =  
+        { 0x17, 0x08, 0x21, 0x1b, 0x2d, 0x4c, 0x1b, 0x00 };
+/* Japaneese (ISO-2022-JP ?) */
+static const unsigned char packet_send_charset_iso_2022_jp[] = 
+        { 0x17, 0x08, 0x21, 0x1b, 0x29, 0x49, 0x1b, 0x7e };
+
 static const unsigned char packet_send_led_update[] = { 0x19, 0x04, 0x00, 0x00 };
 
 static const unsigned char packet_send_query_basic_manager_04[] = { 0x1a, 0x04, 0x01, 0x04 };
@@ -717,14 +743,130 @@
 	.bridge = ast_rtp_instance_bridge,
 };
 
+static void send_start_rtp(struct unistim_subchannel *);
+
+static void send_callerid_screen(struct unistim_subchannel *sub);
+static void key_favorite(struct unistimsession *, char);
+
+static void handle_select_codec(struct unistimsession *);
+static void handle_select_contrast(struct unistimsession *);
+static void handle_select_language(struct unistimsession *);
+static int find_language(const char*);
+
+struct unistim_menu_item options_menu[] =
+{
+    {"Change codec", STATE_SELECTCODEC, handle_select_codec},
+    {"Contrast", STATE_SELECTCONTRAST, handle_select_contrast},
+    {"Language", STATE_SELECTLANGUAGE, handle_select_language},
+    {NULL, 0, NULL}
+};
+
+struct unistim_languages options_languages[] =
+{
+    {"English", "en", ISO_8859_1, NULL},
+    {"Russian", "ru", ISO_8859_5, NULL},
+    {NULL, NULL, 0, NULL}
+};
+
+struct ustm_lang_entry {
+    const char *str_orig;
+    const char *str_trans;
+};
+
+static void lang_entry_destructor(void *obj)
+{
+	//struct ustm_lang_entry *lang_entry = obj;
+        return;
+}
+
+static int lang_hash_fn(const void *obj, const int flags)
+{
+	const struct ustm_lang_entry *entry = obj;
+	return ast_str_hash(entry->str_orig);
+}
+
+static int lang_cmp_fn(void *obj, void *arg, int flags)
+{
+	struct ustm_lang_entry *entry1 = obj;
+	struct ustm_lang_entry *entry2 = arg;
+
+	return (!strcmp(entry1->str_orig, entry2->str_orig)) ? (CMP_MATCH | CMP_STOP) : 0;
+}
+
+static const char *ustmtext(const char *str, struct unistimsession *pte) {
+        struct ustm_lang_entry *lang_entry;
+        struct ustm_lang_entry le_search;
+        struct unistim_languages *lang;
+
+        lang = &options_languages[find_language(pte->device->language)];
+        if (!lang)
+                return str;
+        /* Check if specified language exists */
+        if (!lang->trans) {
+                char tmp[1024], *p, *p_orig = NULL, *p_trans = NULL;
+                FILE *f;
+
+                lang->trans = ao2_container_alloc(8, lang_hash_fn, lang_cmp_fn);
+                snprintf(tmp, sizeof(tmp), "%s/%s/%s.po", ast_config_AST_VAR_DIR,
+			 USTM_LANG_DIR, lang->lang_short);
+                f = fopen(tmp, "r");
+                if (!f) {
+                    return str;
+                }
+                while (fgets(tmp, sizeof(tmp), f)) {
+                     if (!(p = strchr(tmp, '\n'))) {
+                             ast_log(LOG_ERROR, "input line too long.\n");
+                     }
+                     *p = '\0';
+                     if (tmp == strstr(tmp, "msgid")) {
+                             p_orig = ast_strdup(strchr(tmp, '"') + 1);
+                             p = strchr(p_orig, '"');
+                             *p = '\0';
+                     } else if (tmp == strstr(tmp, "msgstr")) {
+                             p_trans = ast_strdup(strchr(tmp, '"') + 1);
+                             p = strchr(p_trans, '"');
+                             *p = '\0';
+                     } else {
+                            continue;
+                     }
+                     if (!p_trans || !p_orig)
+                            continue;
+                     if (!strlen(p_trans)) {
+                            p_trans = NULL;
+                            p_orig = NULL;
+                            continue;
+                     }
+                     if (!(lang_entry = ao2_alloc(sizeof(*lang_entry), lang_entry_destructor))) {
+                            return str;
+                     }
+                     
+                     lang_entry->str_trans = p_trans;
+                     lang_entry->str_orig = p_orig;
+                     ao2_link(lang->trans, lang_entry);
+                     p_trans = NULL;
+                     p_orig = NULL;
+                }
+
+                fclose(f);
+        }
+
+        le_search.str_orig = ast_strdup(str);
+        lang_entry = ao2_find(lang->trans, &le_search, OBJ_POINTER);
+        //lang_entry = NULL;
+        if (lang_entry)
+                return lang_entry->str_trans;
+
+        return str;
+}
+
 static void display_last_error(const char *sz_msg)
 {
-	time_t cur_time;
+	/*time_t cur_time;
 	
-	time(&cur_time);
+	time(&cur_time);*/
 
 	/* Display the error message */
-	ast_log(LOG_WARNING, "%s %s : (%u) %s\n", ctime(&cur_time), sz_msg, errno,
+	ast_log(LOG_WARNING, "%s : (%u) %s\n", /*ctime(&cur_time),*/ sz_msg, errno,
 			strerror(errno));
 }
 
@@ -950,8 +1092,8 @@
 	BUFFSEND;
 	if (unistimdebug)
 		ast_verb(0, "Sending start timer\n");
-	memcpy(buffsend + SIZE_HEADER, packet_send_StartTimer, sizeof(packet_send_StartTimer));
-	send_client(SIZE_HEADER + sizeof(packet_send_StartTimer), buffsend, pte);
+	memcpy(buffsend + SIZE_HEADER, packet_send_start_timer, sizeof(packet_send_start_timer));
+	send_client(SIZE_HEADER + sizeof(packet_send_start_timer), buffsend, pte);
 }
 
 static void send_stop_timer(struct unistimsession *pte)
@@ -1021,9 +1163,9 @@
 
 /* Positions for favorites
  |--------------------|
- |  5	    2    |
- |  4	    1    |
- |  3	    0    |
+ |  5	         2    | <-- not on screen in i2002
+ |  4	         1    |
+ |  3	         0    |
 */
 
 /* status (icons) : 00 = nothing, 2x/3x = see parser.h, 4x/5x = blink fast, 6x/7x = blink slow */
@@ -1040,44 +1182,115 @@
 	buffsend[10] = pos;
 	buffsend[24] = pos;
 	buffsend[25] = status;
-	i = strlen(text);
+	i = strlen(ustmtext(text, pte));
 	if (i > FAV_MAX_LENGTH)
 		i = FAV_MAX_LENGTH;
-	memcpy(buffsend + FAV_MAX_LENGTH + 1, text, i);
+	memcpy(buffsend + FAV_MAX_LENGTH + 1, ustmtext(text, pte), i);
 	send_client(SIZE_HEADER + sizeof(packet_send_favorite), buffsend, pte);
 }
 
+static void send_favorite_short(unsigned char pos, unsigned char status, struct unistimsession *pte) {
+        send_favorite(pos, status, pte, pte->device->softkeylabel[pos]);
+        return;
+}
+
+static void send_favorite_selected(unsigned char status, struct unistimsession *pte) {
+        if (pte->device->selected != -1)
+                send_favorite(pte->device->selected, status, pte, pte->device->softkeylabel[pte->device->selected]);
+        return;
+}
+
+
 static void refresh_all_favorite(struct unistimsession *pte)
 {
 	int i = 0;
 
 	if (unistimdebug)
 		ast_verb(0, "Refreshing all favorite\n");
-	for (i = 0; i < 6; i++) {
-		if ((pte->device->softkeyicon[i] <= FAV_ICON_HEADPHONES_ONHOLD) &&
-			(pte->device->softkeylinepos != i))
-			send_favorite((unsigned char) i, pte->device->softkeyicon[i] + 1, pte,
-						 pte->device->softkeylabel[i]);
+	for (i = 0; i < FAVNUM; i++) {
+                /* !!! Here icon for the lines displayed */
+		if ((pte->device->softkeyicon[i] <= FAV_ICON_HEADPHONES_ONHOLD)/* &&
+			(pte->device->softkeylinepos != i)*/)
+			send_favorite_short((unsigned char) i, pte->device->softkeyicon[i]/* + 1*/, pte);
 		else
-			send_favorite((unsigned char) i, pte->device->softkeyicon[i], pte,
-						 pte->device->softkeylabel[i]);
+			send_favorite_short((unsigned char) i, pte->device->softkeyicon[i], pte);
 
 	}
 }
 
+static int is_key_favorite(struct unistim_device *d, int fav)
+{
+        if ((fav < 0) && (fav > 5))
+		return 0;
+	if (d->softkeyicon[fav] == 0)
+		return 0;
+        if (d->softkeynumber[fav][0] == '\0')
+                return 0;
+        return 1;
+}
+
+static int is_key_line(struct unistim_device *d, int fav)
+{
+        if ((fav < 0) && (fav > 5))
+		return 0;
+        if (d->softkeyicon[fav] == 0)
+		return 0;
+        if (is_key_favorite(d, fav))
+                return 0;
+
+        return 1;
+}
+
+static int soft_key_visible(struct unistim_device* d, int num)
+{
+        if(d->height == 1 && num%3 == 2) {
+            return 0;
+        }
+        return 1;
+}
+
+static int get_active_softkey(struct unistimsession *pte)
+{
+        return pte->device->selected;
+}
+
+static int get_avail_softkey(struct unistimsession *pte)
+{
+        int i;
+
+        if (!is_key_line(pte->device, pte->device->selected)) {
+                pte->device->selected = -1;
+        }
+        for (i = 0; i < FAVNUM; i++) {
+                if (pte->device->selected != -1 && pte->device->selected != i) {
+                    continue;
+                }
+                if (!soft_key_visible(pte->device, i))
+                        continue;
+                if (pte->device->ssub[i])
+                        continue;
+                if (is_key_line(pte->device, i))
+                        return i;
+        }
+        return pte->device->selected;
+}
+
+
 /* Change the status for this phone (pte) and update for each phones where pte is bookmarked
  * use FAV_ICON_*_BLACK constant in status parameters */
 static void change_favorite_icon(struct unistimsession *pte, unsigned char status)
 {
 	struct unistim_device *d = devices;
 	int i;
-	/* Update the current phone */
-	if (pte->state != STATE_CLEANING)
-		send_favorite(pte->device->softkeylinepos, status, pte,
-					 pte->device->softkeylabel[pte->device->softkeylinepos]);
+	/* Update the current phone line softkey icon */
+	if (pte->state != STATE_CLEANING) {
+                int softkeylinepos = get_active_softkey(pte);
+                if (softkeylinepos != -1)
+                        send_favorite_short(softkeylinepos, status, pte);
+        }
 	/* Notify other phones if we're in their bookmark */
 	while (d) {
-		for (i = 0; i < 6; i++) {
+		for (i = 0; i < FAVNUM; i++) {
 			if (d->sp[i] == pte->device) {  /* It's us ? */
 				if (d->softkeyicon[i] != status) {      /* Avoid resending the same icon */
 					d->softkeyicon[i] = status;
@@ -1090,30 +1303,39 @@
 	}
 }
 
-static int RegisterExtension(const struct unistimsession *pte)
+static struct unistim_line* get_main_line(const struct unistim_device *d)
 {
+    return AST_LIST_FIRST(&d->lines);
+}
+
+static int register_extension(const struct unistimsession *pte)
+{
+        struct unistim_line *line;
+        line = get_main_line(pte->device);
 	if (unistimdebug)
 		ast_verb(0, "Trying to register extension '%s' into context '%s' to %s\n",
-					pte->device->extension_number, pte->device->lines->context,
-					pte->device->lines->fullname);
-	return ast_add_extension(pte->device->lines->context, 0,
+					pte->device->extension_number, line->context,
+					line->fullname);
+	return ast_add_extension(line->context, 0,
 							 pte->device->extension_number, 1, NULL, NULL, "Dial",
-							 pte->device->lines->fullname, 0, "Unistim");
+							 line->fullname, 0, "Unistim");
 }
 
 static int UnregisterExtension(const struct unistimsession *pte)
 {
+        struct unistim_line *line;
+        line = get_main_line(pte->device);
 	if (unistimdebug)
 		ast_verb(0, "Trying to unregister extension '%s' context '%s'\n",
-					pte->device->extension_number, pte->device->lines->context);
-	return ast_context_remove_extension(pte->device->lines->context,
+					pte->device->extension_number, line->context);
+	return ast_context_remove_extension(line->context,
 										pte->device->extension_number, 1, "Unistim");
 }
 
 /* Free memory allocated for a phone */
 static void close_client(struct unistimsession *s)
 {
-	struct unistim_subchannel *sub;
+	struct unistim_subchannel *sub = NULL;
 	struct unistimsession *cur, *prev = NULL;
 	ast_mutex_lock(&sessionlock);
 	cur = sessions;
@@ -1127,12 +1349,11 @@
 	if (cur) {				      /* Session found ? */
 		if (cur->device) {	      /* This session was registered ? */
 			s->state = STATE_CLEANING;
-			if (unistimdebug)
+                        /* !!! sub seems to be never used and uninitialized */
+                        if (unistimdebug)
 				ast_verb(0, "close_client session %p device %p lines %p sub %p\n",
-							s, s->device, s->device->lines,
-							s->device->lines->subs[SUB_REAL]);
+							s, s->device, get_main_line(s->device), sub);
 			change_favorite_icon(s, FAV_ICON_NONE);
-			sub = s->device->lines->subs[SUB_REAL];
 			if (sub) {
 				if (sub->owner) {       /* Call in progress ? */
 					if (unistimdebug)
@@ -1206,6 +1427,10 @@
 {
 	int i;
 	BUFFSEND;
+        if (!text) {
+            ast_log(LOG_ERROR, "Asked to display NULL text (pos %d, inverse flag %d)\n", pos, inverse);
+            return;
+        }
 	if (unistimdebug)
 		ast_verb(0, "Sending text at pos %d, inverse flag %d\n", pos, inverse);
 	memcpy(buffsend + SIZE_HEADER, packet_send_text, sizeof(packet_send_text));
@@ -1365,12 +1590,23 @@
 
 }
 
+static void send_idle_clock(struct unistimsession *pte)
+{
+	char text[2];
+
+	text[0] = 0x00;
+	text[1] = 0x00;
+
+        send_text(TEXT_LINE0, TEXT_NORMAL, pte, text);
+}
+
 static void send_date_time(struct unistimsession *pte)
 {
 	BUFFSEND;
 	struct timeval now = ast_tvnow();
 	struct ast_tm atm = { 0, };
 
+        ast_log(LOG_WARNING, "Time Received\n");
 	if (unistimdebug)
 		ast_verb(0, "Sending Time & Date\n");
 	memcpy(buffsend + SIZE_HEADER, packet_send_date_time, sizeof(packet_send_date_time));
@@ -1443,6 +1679,43 @@
 	return;
 }
 
+static void send_charset_update(struct unistimsession *pte, int charset)
+{
+        const unsigned char* packet_send_charset;
+        int packet_size;
+	BUFFSEND;
+	if (unistimdebug)
+		ast_verb(0, "Sending set default charset\n");
+        if (charset == LANG_DEFAULT) {
+            charset = options_languages[find_language(pte->device->language)].encoding;
+        }
+        switch (charset) {
+            case ISO_8859_2:
+                packet_send_charset = packet_send_charset_iso_8859_2;
+                packet_size = sizeof(packet_send_charset_iso_8859_2);
+                break;
+            case ISO_8859_4:
+                packet_send_charset = packet_send_charset_iso_8859_4;
+                packet_size = sizeof(packet_send_charset_iso_8859_4);
+                break;
+            case ISO_8859_5:
+                packet_send_charset = packet_send_charset_iso_8859_5;
+                packet_size = sizeof(packet_send_charset_iso_8859_5);
+                break;
+            case ISO_2022_JP:
+                packet_send_charset = packet_send_charset_iso_2022_jp;
+                packet_size = sizeof(packet_send_charset_iso_2022_jp);
+                break;
+            case ISO_8859_1:
+            default:
+                packet_send_charset = packet_send_charset_iso_8859_1;
+                packet_size = sizeof(packet_send_charset_iso_8859_1);
+        }
+	memcpy(buffsend + SIZE_HEADER, packet_send_charset, packet_size);
+	send_client(SIZE_HEADER + packet_size, buffsend, pte);
+	return;
+}
+
 static void rcv_resume_connection_with_server(struct unistimsession *pte)
 {
 	BUFFSEND;
@@ -1483,35 +1756,43 @@
 	return 1;
 }
 
-static int alloc_sub(struct unistim_line *l, int x)
+static struct unistim_subchannel *alloc_sub(struct unistim_device *d, int x)
 {
 	struct unistim_subchannel *sub;
 	if (!(sub = ast_calloc(1, sizeof(*sub))))
-		return 0;
+		return NULL;
 
 	if (unistimdebug)
-		ast_verb(3, "Allocating UNISTIM subchannel #%d on %s@%s ptr=%p\n", x, l->name, l->parent->name, sub);
-	sub->parent = l;
+		ast_verb(3, "Allocating UNISTIM subchannel #%d on %s ptr=%p\n", x, d->name, sub);
+	sub->parent = NULL;
 	sub->subtype = x;
-	l->subs[x] = sub;
+        AST_LIST_LOCK(&d->subs);
+        AST_LIST_INSERT_TAIL(&d->subs, sub, list);
+        AST_LIST_UNLOCK(&d->subs);
 	ast_mutex_init(&sub->lock);
-	return 1;
+	return sub;
 }
 
-static int unalloc_sub(struct unistim_line *p, int x)
+static int unalloc_sub(struct unistim_device *d, struct unistim_subchannel *sub)
 {
-	if (!x) {
-		ast_log(LOG_WARNING, "Trying to unalloc the real channel %s@%s?!?\n", p->name,
-				p->parent->name);
-		return -1;
-	}
-	if (unistimdebug)
-		ast_debug(1, "Released sub %d of channel %s@%s\n", x, p->name,
-				p->parent->name);
-	ast_mutex_destroy(&p->lock);
-	ast_free(p->subs[x]);
-	p->subs[x] = 0;
-	return 0;
+        struct unistim_subchannel *s;
+
+        AST_LIST_LOCK(&d->subs);
+        AST_LIST_TRAVERSE_SAFE_BEGIN(&d->subs, s, list) {
+                if (!s)
+                        continue;
+                if (s != sub)
+                        continue;
+                AST_LIST_REMOVE_CURRENT(list);
+                if (unistimdebug)
+                	ast_debug(1, "Released sub %d of channel %s@%s\n", sub->subtype, sub->parent->name, d->name);
+
+                ast_free(sub);
+        }
+        AST_LIST_TRAVERSE_SAFE_END;
+        AST_LIST_UNLOCK(&d->subs);
+	//ast_mutex_destroy(&p->lock); TODO
+        return 0;
 }
 
 static void rcv_mac_addr(struct unistimsession *pte, const unsigned char *buf)
@@ -1547,6 +1828,7 @@
 				d = devices;
 				while (d) {
 					if (!strcasecmp(d->name, "template")) {
+                                                struct unistim_line *l;
 						/* Found, cloning this entry */
 						if (!(newd = ast_malloc(sizeof(*newd)))) {
 							ast_mutex_unlock(&devicelock);
@@ -1560,8 +1842,9 @@
 							return;
 						}
 
-						memcpy(newl, d->lines, sizeof(*newl));
-						if (!alloc_sub(newl, SUB_REAL)) {
+                                                l = get_main_line(d);
+						memcpy(newl, l, sizeof(*newl));
+						if (!alloc_sub(newd, SUB_REAL)) {
 							ast_free(newd);
 							ast_free(newl);
 							ast_mutex_unlock(&devicelock);
@@ -1572,16 +1855,16 @@
 						ast_copy_string(newd->name, addrmac, sizeof(newd->name));
 						if (newd->extension == EXTENSION_NONE)
 							newd->extension = EXTENSION_ASK;
-						newd->lines = newl;
+						//newd->lines = newl; TODO
 						newd->receiver_state = STATE_ONHOOK;
 						newd->session = pte;
 						newd->to_delete = -1;
 						pte->device = newd;
 						newd->next = NULL;
 						newl->parent = newd;
-						strcpy(newl->name, d->lines->name);
-						snprintf(d->lines->name, sizeof(d->lines->name), "%d",
-								 atoi(d->lines->name) + 1);
+						strcpy(newl->name, l->name);
+						snprintf(l->name, sizeof(l->name), "%d",
+								 atoi(l->name) + 1);
 						snprintf(newl->fullname, sizeof(newl->fullname), "USTM/%s@%s",
 								 newl->name, newd->name);
 						/* Go to the end of the linked chain */
@@ -1625,16 +1908,16 @@
 				pte->state = STATE_EXTENSION;
 			else {
 				/* Yes, because of a phone reboot. We don't ask again for the TN */
-				if (RegisterExtension(pte))
+				if (register_extension(pte))
 					pte->state = STATE_EXTENSION;
 				else
 					pte->state = STATE_MAINPAGE;
 			}
 			break;
 		case EXTENSION_LINE:
-			ast_copy_string(pte->device->extension_number, pte->device->lines->name,
+			ast_copy_string(pte->device->extension_number, get_main_line(pte->device)->name,
 							sizeof(pte->device->extension_number));
-			if (RegisterExtension(pte))
+			if (register_extension(pte))
 				pte->state = STATE_EXTENSION;
 			else
 				pte->state = STATE_MAINPAGE;
@@ -1859,32 +2142,32 @@
 	return;
 }
 
-static void swap_subs(struct unistim_line *p, int a, int b)
+static void swap_subs(struct unistim_device *d, int a, int b)
 {
 /*  struct ast_channel *towner; */
-	struct ast_rtp_instance *rtp;
-	int fds;
+	//struct ast_rtp_instance *rtp;
+	//int fds;
 
-	if (unistimdebug)
+/*	if (unistimdebug)
 		ast_verb(0, "Swapping %d and %d\n", a, b);
 
-	if ((!p->subs[a]->owner) || (!p->subs[b]->owner)) {
+	if ((!d->subs[a]->owner) || (!d->subs[b]->owner)) {
 		ast_log(LOG_WARNING,
 				"Attempted to swap subchannels with a null owner : sub #%d=%p sub #%d=%p\n",
-				a, p->subs[a]->owner, b, p->subs[b]->owner);
+				a, d->subs[a]->owner, b, d->subs[b]->owner);
 		return;
 	}
-	rtp = p->subs[a]->rtp;
-	p->subs[a]->rtp = p->subs[b]->rtp;
-	p->subs[b]->rtp = rtp;
+	rtp = d->subs[a]->rtp;
+	d->subs[a]->rtp = d->subs[b]->rtp;
+	d->subs[b]->rtp = rtp;
 
-	fds = p->subs[a]->owner->fds[0];
-	p->subs[a]->owner->fds[0] = p->subs[b]->owner->fds[0];
-	p->subs[b]->owner->fds[0] = fds;
+	fds = d->subs[a]->owner->fds[0];
+	d->subs[a]->owner->fds[0] = d->subs[b]->owner->fds[0];
+	d->subs[b]->owner->fds[0] = fds;
 
-	fds = p->subs[a]->owner->fds[1];
-	p->subs[a]->owner->fds[1] = p->subs[b]->owner->fds[1];
-	p->subs[b]->owner->fds[1] = fds;
+	fds = d->subs[a]->owner->fds[1];
+	d->subs[a]->owner->fds[1] = d->subs[b]->owner->fds[1];
+	d->subs[b]->owner->fds[1] = fds;*/
 }
 
 static int attempt_transfer(struct unistim_subchannel *p1, struct unistim_subchannel *p2)
@@ -1972,32 +2255,117 @@
 	memcpy(data, callerid, size);
 }
 
+static struct unistim_subchannel* get_sub(struct unistim_device *device, int type) {
+        struct unistim_subchannel *sub = NULL;
+
+        AST_LIST_LOCK(&device->subs);
+        AST_LIST_TRAVERSE(&device->subs, sub, list) {
+                if (!sub)
+                        continue;
+                if (sub->subtype == type) {
+                        break;
+                }
+        }
+        AST_LIST_UNLOCK(&device->subs);
+
+        return sub;
+}
+
+static void sub_start_silence(struct unistimsession *pte, struct unistim_subchannel *sub) {
+	/* Silence our channel */
+	if (!pte->device->silence_generator) {
+		pte->device->silence_generator =
+			ast_channel_start_silence_generator(sub->owner);
+		if (pte->device->silence_generator == NULL)
+			ast_log(LOG_WARNING, "Unable to start a silence generator.\n");
+		else if (unistimdebug)
+			ast_verb(0, "Starting silence generator\n");
+	}
+
+}
+
+static void sub_stop_silence(struct unistimsession *pte, struct unistim_subchannel *sub) {
+        /* Stop the silence generator */
+        if (pte->device->silence_generator) {
+                if (unistimdebug)
+                        ast_verb(0, "Stopping silence generator\n");
+                if (sub->owner)
+                        ast_channel_stop_silence_generator(sub->owner, pte->device->silence_generator);
+                else
+                        ast_log(LOG_WARNING, "Trying to stop silence generator on a null channel !\n");
+                pte->device->silence_generator = NULL;
+        }
+}
+
+static void sub_hold(struct unistimsession *pte, struct unistim_subchannel *sub) {
+        if (!sub) {
+                return;
+        }
+        sub->moh = 1;
+        sub->subtype = SUB_ONHOLD;
+        send_favorite_short(sub->softkey, FAV_ICON_ONHOLD_BLACK, pte);
+        //send_select_output(pte, pte->device->output, pte->device->volume, MUTE_ON);
+        if (sub->owner) {
+                ast_queue_control_data(sub->owner, AST_CONTROL_HOLD, NULL, 0);
+                send_end_call(pte);
+        }
+        return;
+}
+
+static void sub_unhold(struct unistimsession *pte, struct unistim_subchannel *sub) {
+        struct unistim_subchannel *sub_real;
+        int pos = sub->softkey;
+
+        sub_real = get_sub(sub->parent->parent, SUB_REAL);
+        if (sub_real) {
+            sub_hold(pte, sub_real);
+        }
+
+        sub->moh = 0;
+        sub->subtype = SUB_REAL;
+        send_favorite_short(pos, FAV_ICON_OFFHOOK_BLACK, pte);
+        //send_select_output(pte, pte->device->output, pte->device->volume, MUTE_OFF);
+        if (sub->owner) {
+                ast_queue_control_data(sub->owner, AST_CONTROL_UNHOLD, NULL, 0);
+                if (sub->rtp)
+                        send_start_rtp(sub);
+        }
+        return;
+}
+
 static void close_call(struct unistimsession *pte)
 {
-	struct unistim_subchannel *sub;
-	struct unistim_line *l = pte->device->lines;
+	struct unistim_subchannel *sub, *sub_transf;
 
-	sub = pte->device->lines->subs[SUB_REAL];
+	sub = get_sub(pte->device, SUB_REAL);
+        sub_transf = get_sub(pte->device, SUB_THREEWAY);
 	send_stop_timer(pte);
+        if (!sub) {
+                ast_log(LOG_WARNING, "Close call without sub\n");
+                return;
+        }
+        send_favorite_short(sub->softkey, FAV_ICON_ONHOOK_BLACK, pte);
 	if (sub->owner) {
 		sub->alreadygone = 1;
-		if (l->subs[SUB_THREEWAY]) {
-			l->subs[SUB_THREEWAY]->alreadygone = 1;
-			if (attempt_transfer(sub, l->subs[SUB_THREEWAY]) < 0)
+		if (sub_transf) {
+			sub_transf->alreadygone = 1;
+			if (attempt_transfer(sub, sub_transf) < 0)
 				ast_verb(0, "attempt_transfer failed.\n");
 		} else
 			ast_queue_hangup(sub->owner);
 	} else {
-		if (l->subs[SUB_THREEWAY]) {
-			if (l->subs[SUB_THREEWAY]->owner)
-				ast_queue_hangup_with_cause(l->subs[SUB_THREEWAY]->owner, AST_CAUSE_NORMAL_CLEARING);
+		if (sub_transf) {
+			if (sub_transf->owner)
+				ast_queue_hangup_with_cause(sub_transf->owner, AST_CAUSE_NORMAL_CLEARING);
 			else
 				ast_log(LOG_WARNING, "threeway sub without owner\n");
 		} else
 			ast_verb(0, "USTM(%s@%s-%d) channel already destroyed\n", sub->parent->name,
 						sub->parent->parent->name, sub->subtype);
-	}
-	change_callerid(pte, 0, pte->device->redial_number);
+	}        
+	send_led_update(pte, 0x08);
+	send_led_update(pte, 0x10);
+        change_callerid(pte, 0, pte->device->redial_number);
 	change_callerid(pte, 1, "");
 	write_history(pte, 'o', pte->device->missed_call);
 	pte->device->missed_call = 0;
@@ -2008,6 +2376,7 @@
 static void IgnoreCall(struct unistimsession *pte)
 {
 	send_no_ring(pte);
+        //ast_string_field_set(pte->device->subs[0]->owner, call_forward, "1");
 	return;
 }
 
@@ -2032,80 +2401,46 @@
 	return NULL;
 }
 
-static void start_rtp(struct unistim_subchannel *sub)
-{
+static int find_rtp_port(struct unistim_subchannel *s) {
+        struct unistim_subchannel *sub = NULL;
+        int rtp_start = s->parent->parent->rtp_port;
+        struct ast_sockaddr us_tmp;
+        struct sockaddr_in us = { 0, };
+
+        AST_LIST_LOCK(&s->parent->parent->subs);
+        AST_LIST_TRAVERSE(&s->parent->parent->subs, sub, list) {
+                if (!sub)
+                        continue;
+                if (sub->rtp) {
+                        ast_rtp_instance_get_remote_address(sub->rtp, &us_tmp);
+                        ast_sockaddr_to_sin(&us_tmp, &us);
+                        if (htons(us.sin_port)) {
+                                rtp_start = htons(us.sin_port) + 1;
+                                break;
+                        }
+                }
+        }
+        AST_LIST_UNLOCK(&s->parent->parent->subs);
+
+        ast_log(LOG_WARNING, "Port %d\n", rtp_start);
+        return rtp_start;
+}
+
+static void send_start_rtp(struct unistim_subchannel *sub) {
 	BUFFSEND;
+
+	format_t codec;
+	struct sockaddr_in public = { 0, };
 	struct sockaddr_in us = { 0, };
-	struct sockaddr_in public = { 0, };
 	struct sockaddr_in sin = { 0, };
-	format_t codec;
-	struct sockaddr_in sout = { 0, };
 	struct ast_sockaddr us_tmp;
 	struct ast_sockaddr sin_tmp;
-	struct ast_sockaddr sout_tmp;
 
-	/* Sanity checks */
-	if (!sub) {
-		ast_log(LOG_WARNING, "start_rtp with a null subchannel !\n");
-		return;
-	}
-	if (!sub->parent) {
-		ast_log(LOG_WARNING, "start_rtp with a null line !\n");
-		return;
-	}
-	if (!sub->parent->parent) {
-		ast_log(LOG_WARNING, "start_rtp with a null device !\n");
-		return;
-	}
-	if (!sub->parent->parent->session) {
-		ast_log(LOG_WARNING, "start_rtp with a null session !\n");
-		return;
-	}
-	sout = sub->parent->parent->session->sout;
-
-	ast_mutex_lock(&sub->lock);
-	/* Allocate the RTP */
-	if (unistimdebug)
-		ast_verb(0, "Starting RTP. Bind on %s\n", ast_inet_ntoa(sout.sin_addr));
-	ast_sockaddr_from_sin(&sout_tmp, &sout);
-	sub->rtp = ast_rtp_instance_new("asterisk", sched, &sout_tmp, NULL);
-	if (!sub->rtp) {
-		ast_log(LOG_WARNING, "Unable to create RTP session: %s binaddr=%s\n",
-				strerror(errno), ast_inet_ntoa(sout.sin_addr));
-		ast_mutex_unlock(&sub->lock);
-		return;
-	}
-	ast_rtp_instance_set_prop(sub->rtp, AST_RTP_PROPERTY_RTCP, 1);
-	if (sub->owner) {
-		sub->owner->fds[0] = ast_rtp_instance_fd(sub->rtp, 0);
-		sub->owner->fds[1] = ast_rtp_instance_fd(sub->rtp, 1);
-	}
-	ast_rtp_instance_set_qos(sub->rtp, qos.tos_audio, qos.cos_audio, "UNISTIM RTP");
-	ast_rtp_instance_set_prop(sub->rtp, AST_RTP_PROPERTY_NAT, sub->parent->parent->nat);
-
-	/* Create the RTP connection */
 	ast_rtp_instance_get_local_address(sub->rtp, &us_tmp);
 	ast_sockaddr_to_sin(&us_tmp, &us);
-	sin.sin_family = AF_INET;
-	/* Setting up RTP for our side */
-	memcpy(&sin.sin_addr, &sub->parent->parent->session->sin.sin_addr,
-		   sizeof(sin.sin_addr));
-	sin.sin_port = htons(sub->parent->parent->rtp_port);
-	ast_sockaddr_from_sin(&sin_tmp, &sin);
-	ast_rtp_instance_set_remote_address(sub->rtp, &sin_tmp);
-	if (!(sub->owner->nativeformats & sub->owner->readformat)) {
-		format_t fmt;
-		char tmp[256];
-		fmt = ast_best_codec(sub->owner->nativeformats);
-		ast_log(LOG_WARNING,
-				"Our read/writeformat has been changed to something incompatible: %s, using %s best codec from %s\n",
-				ast_getformatname(sub->owner->readformat),
-				ast_getformatname(fmt),
-				ast_getformatname_multiple(tmp, sizeof(tmp), sub->owner->nativeformats));
-		sub->owner->readformat = fmt;
-		sub->owner->writeformat = fmt;
-	}
-	codec = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(sub->rtp), 1, sub->owner->readformat);
+	ast_rtp_instance_get_remote_address(sub->rtp, &sin_tmp);
+	ast_sockaddr_to_sin(&sin_tmp, &sin);
+
 	/* Setting up RTP of the phone */
 	if (public_ip.sin_family == 0)  /* NAT IP override ?   */
 		memcpy(&public, &us, sizeof(public));   /* No defined, using IP from recvmsg  */
@@ -2118,7 +2453,9 @@
 		ast_verb(0, "Starting phone RTP stack. Our public IP is %s\n",
 					ast_inet_ntoa(public.sin_addr));
 	}
-	if ((sub->owner->readformat == AST_FORMAT_ULAW) ||
+
+	codec = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(sub->rtp), 1, sub->owner->readformat);
+        if ((sub->owner->readformat == AST_FORMAT_ULAW) ||
 		(sub->owner->readformat == AST_FORMAT_ALAW)) {
 		if (unistimdebug)
 			ast_verb(0, "Sending packet_send_rtp_packet_size for codec %s\n", ast_getformatname(codec));
@@ -2233,70 +2570,150 @@
 		send_client(SIZE_HEADER + sizeof(packet_send_call), buffsend,
 				   sub->parent->parent->session);
 	}
-	ast_mutex_unlock(&sub->lock);
 }
 
-static void SendDialTone(struct unistimsession *pte)
+static void start_rtp(struct unistim_subchannel *sub)
 {
-	int i;
-	/* No country defined ? Using US tone */
-	if (ast_strlen_zero(pte->device->country)) {
-		if (unistimdebug)
-			ast_verb(0, "No country defined, using US tone\n");
-		send_tone(pte, 350, 440);
+	struct sockaddr_in sin = { 0, };
+	struct sockaddr_in sout = { 0, };
+	struct ast_sockaddr sin_tmp;
+	struct ast_sockaddr sout_tmp;
+
+	/* Sanity checks */
+	if (!sub) {
+		ast_log(LOG_WARNING, "start_rtp with a null subchannel !\n");
 		return;
 	}
-	if (strlen(pte->device->country) != 2) {
-		if (unistimdebug)
-			ast_verb(0, "Country code != 2 char, using US tone\n");
-		send_tone(pte, 350, 440);
+	if (!sub->parent) {
+		ast_log(LOG_WARNING, "start_rtp with a null line !\n");
 		return;
 	}
-	i = 0;
-	while (frequency[i].freq1) {
-		if ((frequency[i].country[0] == pte->device->country[0]) &&
-			(frequency[i].country[1] == pte->device->country[1])) {
-			if (unistimdebug)
-				ast_verb(0, "Country code found (%s), freq1=%d freq2=%d\n",
-							frequency[i].country, frequency[i].freq1, frequency[i].freq2);
-			send_tone(pte, frequency[i].freq1, frequency[i].freq2);
-		}
-		i++;
+	if (!sub->parent->parent) {
+		ast_log(LOG_WARNING, "start_rtp with a null device !\n");
+		return;
 	}
+	if (!sub->parent->parent->session) {
+		ast_log(LOG_WARNING, "start_rtp with a null session !\n");
+		return;
+	}
+        if (!sub->owner) {
+		ast_log(LOG_WARNING, "start_rtp with a null asterisk channel !\n");
+		return;
+	}
+	sout = sub->parent->parent->session->sout;
+        sout.sin_family = AF_INET;
+	ast_mutex_lock(&sub->lock);
+	/* Allocate the RTP */
+	if (unistimdebug)
+		ast_verb(0, "Starting RTP. Bind on %s\n", ast_inet_ntoa(sout.sin_addr));
+	ast_sockaddr_from_sin(&sout_tmp, &sout);
+	sub->rtp = ast_rtp_instance_new("asterisk", sched, &sout_tmp, NULL);
+	if (!sub->rtp) {
+		ast_log(LOG_WARNING, "Unable to create RTP session: %s binaddr=%s\n",
+				strerror(errno), ast_inet_ntoa(sout.sin_addr));
+		ast_mutex_unlock(&sub->lock);
+		return;
+	}
+	ast_rtp_instance_set_prop(sub->rtp, AST_RTP_PROPERTY_RTCP, 1);
+	sub->owner->fds[0] = ast_rtp_instance_fd(sub->rtp, 0);
+	sub->owner->fds[1] = ast_rtp_instance_fd(sub->rtp, 1);
+	ast_rtp_instance_set_qos(sub->rtp, qos.tos_audio, qos.cos_audio, "UNISTIM RTP");
+	ast_rtp_instance_set_prop(sub->rtp, AST_RTP_PROPERTY_NAT, sub->parent->parent->nat);
+
+	/* Create the RTP connection */
+	sin.sin_family = AF_INET;
+	/* Setting up RTP for our side */
+	memcpy(&sin.sin_addr, &sub->parent->parent->session->sin.sin_addr,
+		   sizeof(sin.sin_addr));
+
+	sin.sin_port = htons(find_rtp_port(sub));
+	ast_sockaddr_from_sin(&sin_tmp, &sin);
+	ast_rtp_instance_set_remote_address(sub->rtp, &sin_tmp);
+	if (!(sub->owner->nativeformats & sub->owner->readformat)) {
+		format_t fmt;
+		char tmp[256];
+		fmt = ast_best_codec(sub->owner->nativeformats);
+		ast_log(LOG_WARNING,
+				"Our read/writeformat has been changed to something incompatible: %s, using %s best codec from %s\n",
+				ast_getformatname(sub->owner->readformat),
+				ast_getformatname(fmt),
+				ast_getformatname_multiple(tmp, sizeof(tmp), sub->owner->nativeformats));
+		sub->owner->readformat = fmt;
+		sub->owner->writeformat = fmt;
+	}
+
+        send_start_rtp(sub);
+
+	ast_mutex_unlock(&sub->lock);
 }
 
+static void SendDialTone(struct unistimsession *pte)
+{
+        struct ast_tone_zone_sound *ts = NULL;
+        struct ast_tone_zone_part tone_data;
+        char *s = NULL;
+        char *ind;
+
+	if ((ts = ast_get_indication_tone(pte->device->tz, "dial"))) {
+                ind = ast_strdupa(ts->data);
+                s = strsep(&ind, ",");
+                ast_tone_zone_part_parse(s, &tone_data);
+                if (tone_data.modulate)
+                        tone_data.freq2 = 0;
+
+                send_tone(pte, tone_data.freq1, tone_data.freq2);
+                if (unistimdebug)
+			ast_verb(0, "Country code found (%s), freq1=%d freq2=%d\n",
+							pte->device->tz->country, tone_data.freq1, tone_data.freq2);
+
+                ts = ast_tone_zone_sound_unref(ts);
+	}
+}
+
+static void show_phone_number(struct unistimsession *pte)
+{
+        char tmp[TEXT_LENGTH_MAX + 1];
+        const char *tmp_number = ustmtext("Number:", pte);
+        int line, offset = 0, i;
+
+        pte->device->phone_number[pte->device->size_phone_number] = '\0';
+        if  (pte->device->size_phone_number > MAX_SCREEN_NUMBER) {
+                offset = pte->device->size_phone_number - MAX_SCREEN_NUMBER - 1;
+                if (offset > strlen(tmp_number)) 
+                        offset = strlen(tmp_number);
+                memcpy(tmp, tmp_number + offset, strlen(tmp_number) - offset + 1);
+        } else {
+                strcpy(tmp, tmp_number);
+        }
+
+        offset = (pte->device->size_phone_number >= TEXT_LENGTH_MAX) ? (pte->device->size_phone_number - TEXT_LENGTH_MAX +1) : 0;
+        if (pte->device->size_phone_number)
+                memcpy(tmp + strlen(tmp), pte->device->phone_number + offset, pte->device->size_phone_number - offset + 1);
+        offset = strlen(tmp);
+
+        for (i = strlen(tmp); i < TEXT_LENGTH_MAX; i++) {
+            tmp[i] = '.';
+        }
+        tmp[i] = '\0';
+
+        line = (pte->device->height == 1) ? TEXT_LINE0 : TEXT_LINE2;
+        send_text(line, TEXT_NORMAL, pte, tmp);
+        send_blink_cursor(pte);
+        send_cursor_pos(pte, (unsigned char) (line + offset));
+        send_led_update(pte, 0);
+}
+
 static void handle_dial_page(struct unistimsession *pte)
 {
 	pte->state = STATE_DIALPAGE;
 	if (pte->device->call_forward[0] == -1) {
 		send_text(TEXT_LINE0, TEXT_NORMAL, pte, "");
-		send_text(TEXT_LINE1, TEXT_NORMAL, pte, "Enter forward");
-		send_text_status(pte, "ForwardCancel BackSpcErase");
+		send_text(TEXT_LINE1, TEXT_NORMAL, pte, ustmtext("Enter forward", pte));
+		send_text_status(pte, ustmtext("Fwd    Cancel BackSp Erase", pte));
 		if (pte->device->call_forward[1] != 0) {
-			char tmp[TEXT_LENGTH_MAX + 1];
-
 			ast_copy_string(pte->device->phone_number, pte->device->call_forward + 1,
 							sizeof(pte->device->phone_number));
-			pte->device->size_phone_number = strlen(pte->device->phone_number);
-			if (pte->device->size_phone_number > 15)
-				pte->device->size_phone_number = 15;
-			strcpy(tmp, "Number : ...............");
-			memcpy(tmp + 9, pte->device->phone_number, pte->device->size_phone_number);
-
-			if (pte->device->height == 1) {
-				send_text(TEXT_LINE0, TEXT_NORMAL, pte, tmp);
-				send_blink_cursor(pte);
-				send_cursor_pos(pte,
-						  (unsigned char) (TEXT_LINE0 + 0x09 +
-										   pte->device->size_phone_number));
-			} else {
-				send_text(TEXT_LINE2, TEXT_NORMAL, pte, tmp);
-				send_blink_cursor(pte);
-				send_cursor_pos(pte,
-						  (unsigned char) (TEXT_LINE2 + 0x09 +
-										   pte->device->size_phone_number));
-			}
-
+                        show_phone_number(pte);
 			send_led_update(pte, 0);
 			return;
 		}
@@ -2309,24 +2726,16 @@
 		SendDialTone(pte);
 
 		if (pte->device->height > 1) {
-			send_text(TEXT_LINE0, TEXT_NORMAL, pte, "Enter the number to dial");
-			send_text(TEXT_LINE1, TEXT_NORMAL, pte, "and press Call");
+			send_text(TEXT_LINE0, TEXT_NORMAL, pte, ustmtext("Enter the number to dial", pte));
+			send_text(TEXT_LINE1, TEXT_NORMAL, pte, ustmtext("and press Call", pte));
 		}
-		send_text_status(pte, "Call   Redial BackSpcErase");
+		send_text_status(pte, ustmtext("Call   Redial BackSp Erase", pte));
 	}
 
-	if (pte->device->height == 1) {
-		send_text(TEXT_LINE0, TEXT_NORMAL, pte, "Number : ...............");
-		send_blink_cursor(pte);
-		send_cursor_pos(pte, TEXT_LINE0 + 0x09);
-	} else {
-		send_text(TEXT_LINE2, TEXT_NORMAL, pte, "Number : ...............");
-		send_blink_cursor(pte);
-		send_cursor_pos(pte, TEXT_LINE2 + 0x09);
-	}
 	pte->device->size_phone_number = 0;
 	pte->device->phone_number[0] = 0;
-	change_favorite_icon(pte, FAV_ICON_PHONE_BLACK);
+        show_phone_number(pte);
+        change_favorite_icon(pte, FAV_ICON_PHONE_BLACK);
 	Sendicon(TEXT_LINE0, FAV_ICON_NONE, pte);
 	pte->device->missed_call = 0;
 	send_led_update(pte, 0);
@@ -2336,77 +2745,102 @@
 /* Step 1 : Music On Hold for peer, Dialing screen for us */
 static void TransferCallStep1(struct unistimsession *pte)
 {
-	struct unistim_subchannel *sub;
-	struct unistim_line *p = pte->device->lines;
+	struct unistim_subchannel *sub, *sub_trans;
+	struct unistim_device *d = pte->device;
 
-	sub = p->subs[SUB_REAL];
+	sub = get_sub(d, SUB_REAL);
+	sub_trans = get_sub(d, SUB_THREEWAY);
 
 	if (!sub->owner) {
 		ast_log(LOG_WARNING, "Unable to find subchannel for music on hold\n");
 		return;
 	}
-	if (p->subs[SUB_THREEWAY]) {
+	if (sub_trans) {
 		if (unistimdebug)
 			ast_verb(0, "Transfer canceled, hangup our threeway channel\n");
-		if (p->subs[SUB_THREEWAY]->owner)
-			ast_queue_hangup_with_cause(p->subs[SUB_THREEWAY]->owner, AST_CAUSE_NORMAL_CLEARING);
+		if (sub_trans->owner)
+			ast_queue_hangup_with_cause(sub_trans->owner, AST_CAUSE_NORMAL_CLEARING);
 		else
 			ast_log(LOG_WARNING, "Canceling a threeway channel without owner\n");
 		return;
 	}
 	/* Start music on hold if appropriate */
-	if (pte->device->moh)
+	if (sub->moh)
 		ast_log(LOG_WARNING, "Transfer with peer already listening music on hold\n");
 	else {
-		if (ast_bridged_channel(p->subs[SUB_REAL]->owner)) {
-			ast_moh_start(ast_bridged_channel(p->subs[SUB_REAL]->owner),
-						  pte->device->lines->musicclass, NULL);
-			pte->device->moh = 1;
+		if (ast_bridged_channel(sub->owner)) {
+			ast_moh_start(ast_bridged_channel(sub->owner),
+						  sub->parent->musicclass, NULL);
+			sub->moh = 1;
 		} else {
 			ast_log(LOG_WARNING, "Unable to find peer subchannel for music on hold\n");
 			return;
 		}
 	}
-	/* Silence our channel */
-	if (!pte->device->silence_generator) {
-		pte->device->silence_generator =
-			ast_channel_start_silence_generator(p->subs[SUB_REAL]->owner);
-		if (pte->device->silence_generator == NULL)
-			ast_log(LOG_WARNING, "Unable to start a silence generator.\n");
-		else if (unistimdebug)
-			ast_verb(0, "Starting silence generator\n");
-	}
+        sub_start_silence(pte, sub);
 	handle_dial_page(pte);
 }
 
 /* From phone to PBX */
-static void HandleCallOutgoing(struct unistimsession *s)
+static void handle_call_outgoing(struct unistimsession *s)
 {
 	struct ast_channel *c;
 	struct unistim_subchannel *sub;
 	pthread_t t;
 	s->state = STATE_CALL;
-	sub = s->device->lines->subs[SUB_REAL];
+        s->sout.sin_family = AF_INET;
+
+	sub = get_sub(s->device, SUB_REAL);
 	if (!sub) {
-		ast_log(LOG_NOTICE, "No available lines on: %s\n", s->device->name);
-		return;
-	}
+                sub = alloc_sub(s->device, SUB_REAL);
+        }
+        if (sub->owner) { /* have already call assigned */
+                /* Need to put on hold */
+                sub_hold(s, sub);
+                sub = alloc_sub(s->device, SUB_REAL);
+        }
+        if (!sub->parent) {
+                int i = get_avail_softkey(s);
+                sub->parent = s->device->sline[i];
+                s->device->ssub[i] = sub;
+                sub->softkey = i;
+        }
+        send_favorite_short(sub->softkey, FAV_ICON_OFFHOOK_BLACK, s);
+        s->device->selected = -1;
 	if (!sub->owner) {		      /* A call is already in progress ? */
 		c = unistim_new(sub, AST_STATE_DOWN, NULL);   /* No, starting a new one */
-		if (c) {
-			/* Need to start RTP before calling ast_pbx_run */
-			if (!sub->rtp)
-				start_rtp(sub);
+                if (!sub->rtp) /* Need to start RTP before calling ast_pbx_run */
+                        start_rtp(sub);
+                if (!strcmp(s->device->phone_number, ast_pickup_ext())) {
+                        if (unistimdebug)
+                                ast_verb(0, "Try to pickup in unistim_new\n");
+
+                        send_text(TEXT_LINE0, TEXT_NORMAL, s, "");
+                        send_text_status(s, ustmtext("Hangup Transf", s));
+                        send_start_timer(s);
+                        if (ast_pickup_call(c)) {
+                                ast_log(LOG_NOTICE, "Nothing to pick up\n");
+                                c->hangupcause = AST_CAUSE_CALL_REJECTED;
+                        } else {
+                                c->hangupcause = AST_CAUSE_NORMAL_CLEARING;
+                        }
+                        ast_hangup(c);
+                        c = NULL;
+                } else if (c) {
 			send_select_output(s, s->device->output, s->device->volume, MUTE_OFF);
-
+                        send_tone(s, 0, 0); /* Dialing empty number should also stop dial tone */
 			if (s->device->height == 1) {
-				send_text(TEXT_LINE0, TEXT_NORMAL, s, s->device->phone_number);
+                                if (strlen(s->device->phone_number) > 0) {
+                                        send_text(TEXT_LINE0, TEXT_NORMAL, s, s->device->phone_number);
+                                } else {
+                                        send_text(TEXT_LINE0, TEXT_NORMAL, s, ustmtext("Calling...", s));
+                                }
 			} else {
-				send_text(TEXT_LINE0, TEXT_NORMAL, s, "Calling :");
+				send_text(TEXT_LINE0, TEXT_NORMAL, s, ustmtext("Calling :", s));
 				send_text(TEXT_LINE1, TEXT_NORMAL, s, s->device->phone_number);
-				send_text(TEXT_LINE2, TEXT_NORMAL, s, "Dialing...");
+				send_text(TEXT_LINE2, TEXT_NORMAL, s, ustmtext("Dialing...", s));
 			}
-			send_text_status(s, "Hangup");
+			send_text_status(s, ustmtext("Hangup", s));
 
 			/* start switch */
 			if (ast_pthread_create(&t, NULL, unistim_ss, c)) {
@@ -2418,61 +2852,56 @@
 					sub->parent->name, s->device->name);
 	} else {					/* We already have a call, so we switch in a threeway call */
 
-		if (s->device->moh) {
-			struct unistim_subchannel *subchannel;
-			struct unistim_line *p = s->device->lines;
-			subchannel = p->subs[SUB_REAL];
+		if (1/*s->device->moh*/) {
+			struct unistim_subchannel *sub, *sub_trans;
+			struct unistim_device *d = s->device;
+                        struct unistim_line *p;
+			sub = get_sub(d, SUB_REAL);
+                        p = sub->parent;
 
-			if (!subchannel->owner) {
+			if (!sub->owner) {
 				ast_log(LOG_WARNING, "Unable to find subchannel for music on hold\n");
 				return;
 			}
-			if (p->subs[SUB_THREEWAY]) {
+			if (get_sub(d, SUB_THREEWAY)) {
 				ast_log(LOG_WARNING,
 						"Can't transfer while an another transfer is taking place\n");
 				return;
 			}
-			if (!alloc_sub(p, SUB_THREEWAY)) {
+                        sub_trans = alloc_sub(d, SUB_THREEWAY);
+			if (!sub_trans) {
 				ast_log(LOG_WARNING, "Unable to allocate three-way subchannel\n");
 				return;
 			}
-			/* Stop the silence generator */
-			if (s->device->silence_generator) {
-				if (unistimdebug)
-					ast_verb(0, "Stopping silence generator\n");
-				ast_channel_stop_silence_generator(subchannel->owner,
-												   s->device->silence_generator);
-				s->device->silence_generator = NULL;
-			}
-			send_tone(s, 0, 0);
+                        sub_stop_silence(s, sub);
+                        send_tone(s, 0, 0);
 			/* Make new channel */
-			c = unistim_new(p->subs[SUB_THREEWAY], AST_STATE_DOWN, NULL);
+			c = unistim_new(sub_trans, AST_STATE_DOWN, NULL);
 			if (!c) {
 				ast_log(LOG_WARNING, "Cannot allocate new structure on channel %p\n", p);
 				return;
 			}
 			/* Swap things around between the three-way and real call */
-			swap_subs(p, SUB_THREEWAY, SUB_REAL);
+			swap_subs(d, SUB_THREEWAY, SUB_REAL);
 			send_select_output(s, s->device->output, s->device->volume, MUTE_OFF);
 
 			if (s->device->height == 1) {
 				send_text(TEXT_LINE0, TEXT_NORMAL, s, s->device->phone_number);
 			} else {
-				send_text(TEXT_LINE0, TEXT_NORMAL, s, "Calling (pre-transfer)");
+				send_text(TEXT_LINE0, TEXT_NORMAL, s, ustmtext("Calling (pre-transfer)", s));
 				send_text(TEXT_LINE1, TEXT_NORMAL, s, s->device->phone_number);
-				send_text(TEXT_LINE2, TEXT_NORMAL, s, "Dialing...");
+				send_text(TEXT_LINE2, TEXT_NORMAL, s, ustmtext("Dialing...", s));
 			}
-			send_text_status(s, "TransfrCancel");
+			send_text_status(s, ustmtext("TransfrCancel", s));
 
-			if (ast_pthread_create(&t, NULL, unistim_ss, p->subs[SUB_THREEWAY]->owner)) {
+			if (ast_pthread_create(&t, NULL, unistim_ss, get_sub(d, SUB_THREEWAY)->owner)) {
 				ast_log(LOG_WARNING, "Unable to start simple switch on channel %p\n", p);
 				ast_hangup(c);
 				return;
 			}
 			if (unistimdebug)
 				ast_verb(0, "Started three way call on channel %p (%s) subchan %d\n",
-					 p->subs[SUB_THREEWAY]->owner, p->subs[SUB_THREEWAY]->owner->name,
-					 p->subs[SUB_THREEWAY]->subtype);
+					 sub_trans->owner, sub_trans->owner->name, sub_trans->subtype);
 		} else
 			ast_debug(1, "Current sub [%s] already has owner\n", sub->owner->name);
 	}
@@ -2480,17 +2909,37 @@
 }
 
 /* From PBX to phone */
-static void HandleCallIncoming(struct unistimsession *s)
+static void handle_call_incoming(struct unistimsession *s)
 {
-	struct unistim_subchannel *sub;
+	struct unistim_subchannel *sub = NULL;
+
 	s->state = STATE_CALL;
 	s->device->missed_call = 0;
 	send_no_ring(s);
-	sub = s->device->lines->subs[SUB_REAL];
+        sub = get_sub(s->device, SUB_RING); /* Put other SUB_REAL call on hold */
 	if (!sub) {
 		ast_log(LOG_NOTICE, "No available lines on: %s\n", s->device->name);
 		return;
-	} else if (unistimdebug)
+	}
+        sub->subtype = SUB_REAL;
+        if (!sub->parent) {
+                int i;
+                if (unistimdebug)
+                        ast_verb(0, "Looking for line we can assign\n");
+                for (i = 0; i < FAVNUM; i++) {
+                        /* XXX Find a first free line/button */
+                        if (!soft_key_visible(s->device, i))
+                                continue;
+                        if (s->device->ssub[i])
+                                continue;
+                        if (is_key_line(s->device, i)) {
+                                sub->parent = s->device->sline[i];
+                                break;
+                        }
+                }
+        }
+    
+        if (unistimdebug)
 		ast_verb(0, "Handle Call Incoming for %s@%s\n", sub->parent->name,
 					s->device->name);
 	start_rtp(sub);
@@ -2498,8 +2947,8 @@
 		ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", sub->parent->name,
 				s->device->name);
 	ast_queue_control(sub->owner, AST_CONTROL_ANSWER);
-	send_text(TEXT_LINE2, TEXT_NORMAL, s, "is on-line");
-	send_text_status(s, "Hangup Transf");
+	send_text(TEXT_LINE2, TEXT_NORMAL, s, ustmtext("is on-line", s));
+	send_text_status(s, ustmtext("Hangup Transf", s));
 	send_start_timer(s);
 
 	if ((s->device->output == OUTPUT_HANDSET) &&
@@ -2507,7 +2956,7 @@
 		send_select_output(s, OUTPUT_SPEAKER, s->device->volume, MUTE_OFF);
 	else
 		send_select_output(s, s->device->output, s->device->volume, MUTE_OFF);
-	s->device->start_call_timestamp = time(0);
+	sub->start_call_timestamp = time(0);
 	write_history(s, 'i', 0);
 	return;
 }
@@ -2516,7 +2965,7 @@
 {
 	struct ast_frame f = { .frametype = AST_FRAME_DTMF, .subclass.integer = digit, .src = "unistim" };
 	struct unistim_subchannel *sub;
-	sub = pte->device->lines->subs[SUB_REAL];
+	sub = get_sub(pte->device, SUB_REAL);
 	if (!sub->owner || sub->alreadygone) {
 		ast_log(LOG_WARNING, "Unable to find subchannel in dtmf senddigit\n");
 		return -1;
@@ -2584,8 +3033,58 @@
 	return 0;
 }
 
+static void handle_key_fav(struct unistimsession *pte, char keycode) {
+        int keynum = keycode - KEY_FAV0;
+        struct unistim_subchannel *sub;
+
+        sub = get_sub(pte->device, SUB_REAL);
+
+        if (0) {
+                /* Key not configured at all - no action */
+                return;
+        }
+        /* Put call on old if pressed same key as */
+        if (sub && sub->softkey == keynum) { 
+                sub_hold(pte, sub);
+                return;
+        }
+
+        /* Make an action on selected fav key */
+        if (!pte->device->ssub[keynum]) {
+                send_favorite_selected(FAV_ICON_ONHOOK_BLACK, pte);
+                sub_hold(pte, sub);
+                if (is_key_line(pte->device, keynum)) {
+                        pte->device->selected = keynum;
+                        send_stop_timer(pte);
+                        handle_dial_page(pte);
+                } else if (is_key_favorite(pte->device, keynum)) {
+                        if ((pte->device->output == OUTPUT_HANDSET) &&
+                                (pte->device->receiver_state == STATE_ONHOOK))
+                                send_select_output(pte, OUTPUT_SPEAKER, pte->device->volume, MUTE_OFF);
+                        else
+                                send_select_output(pte, pte->device->output, pte->device->volume, MUTE_OFF);
+
+                        key_favorite(pte, keycode);
+                }
+        } else {
+                /* Favicon have assigned sub, activate it and put curent on hold */
+                if (pte->device->ssub[keynum]->subtype == SUB_REAL)
+                        sub_hold(pte, pte->device->ssub[keynum]);
+                else {
+                        if (pte->state == STATE_DIALPAGE){
+                                send_tone(pte, 0, 0);
+                        }
+                        send_callerid_screen(sub);
+                        send_start_timer(pte);
+                        sub_unhold(pte, pte->device->ssub[keynum]);
+                        pte->state = STATE_CALL;
+                }
+        }
+}
+
 static void key_call(struct unistimsession *pte, char keycode)
 {
+	struct unistim_subchannel *sub;
 	if ((keycode >= KEY_0) && (keycode <= KEY_SHARP)) {
 		if (keycode == KEY_SHARP)
 			keycode = '#';
@@ -2604,7 +3103,15 @@
 	case KEY_FUNC2:
 		TransferCallStep1(pte);
 		break;
-	case KEY_HEADPHN:
+        case KEY_FAV0:
+        case KEY_FAV1:
+        case KEY_FAV2:
+        case KEY_FAV3:
+        case KEY_FAV4:
+        case KEY_FAV5:
+                handle_key_fav(pte, keycode);
+                break;
+        case KEY_HEADPHN:
 		if (pte->device->output == OUTPUT_HEADPHONE)
 			send_select_output(pte, OUTPUT_HANDSET, pte->device->volume, MUTE_OFF);
 		else
@@ -2618,82 +3125,78 @@
 							 MUTE_OFF);
 		break;
 	case KEY_MUTE:
-		if (!pte->device->moh) {
-			if (pte->device->mute == MUTE_ON)
-				send_select_output(pte, pte->device->output, pte->device->volume, MUTE_OFF);
-			else
-				send_select_output(pte, pte->device->output, pte->device->volume, MUTE_ON);
-			break;
-		}
+                sub = get_sub(pte->device, SUB_REAL);
+                if (!sub || !sub->owner) {
+                        ast_log(LOG_WARNING, "Unable to find subchannel for music on hold\n");
+                        return;
+                }
+                if (!sub->moh) {
+                        if (pte->device->mute == MUTE_ON)
+                                send_select_output(pte, pte->device->output, pte->device->volume, MUTE_OFF);
+                        else
+                                send_select_output(pte, pte->device->output, pte->device->volume, MUTE_ON);
+                        break;
+                }
+                break;
 	case KEY_ONHOLD:
-		{
-			struct unistim_subchannel *sub;
-			struct ast_channel *bridgepeer = NULL;
-			sub = pte->device->lines->subs[SUB_REAL];
-			if (!sub->owner) {
-				ast_log(LOG_WARNING, "Unable to find subchannel for music on hold\n");
-				return;
-			}
-			if ((bridgepeer = ast_bridged_channel(sub->owner))) {
-				if (pte->device->moh) {
-					ast_moh_stop(bridgepeer);
-					pte->device->moh = 0;
-					send_select_output(pte, pte->device->output, pte->device->volume,
-									 MUTE_OFF);
-				} else {
-					ast_moh_start(bridgepeer, pte->device->lines->musicclass, NULL);
-					pte->device->moh = 1;
-					send_select_output(pte, pte->device->output, pte->device->volume,
-									 MUTE_ON);
-				}
-			} else
-				ast_log(LOG_WARNING,
-						"Unable to find peer subchannel for music on hold\n");
-			break;
-		}
+                sub = get_sub(pte->device, SUB_REAL);
+                if (!sub) {
+                        if(pte->device->ssub[pte->device->selected]) {
+                                sub_hold(pte, pte->device->ssub[pte->device->selected]);
+                        }
+                } else
+                        sub_hold(pte, sub);
+                break;
 	}
 	return;
 }
 
 static void key_ringing(struct unistimsession *pte, char keycode)
 {
-	if (keycode == KEY_FAV0 + pte->device->softkeylinepos) {
-		HandleCallIncoming(pte);
-		return;
-	}
 	switch (keycode) {
+        case KEY_FAV0:
+        case KEY_FAV1:
+        case KEY_FAV2:
+        case KEY_FAV3:
+        case KEY_FAV4:
+        case KEY_FAV5:
+                /* If call ringing - only ringing softkey possible to be pressed */
+                if (pte->device->ssub[keycode - KEY_FAV0] && pte->device->ssub[keycode - KEY_FAV0]->subtype == SUB_RING) {
+                        handle_call_incoming(pte);
+                }
+                break;
 	case KEY_HANGUP:
 	case KEY_FUNC4:
 		IgnoreCall(pte);
 		break;
 	case KEY_FUNC1:
-		HandleCallIncoming(pte);
+		handle_call_incoming(pte);
 		break;
 	}
 	return;
 }
 
-static void Keyfavorite(struct unistimsession *pte, char keycode)
+static void key_favorite(struct unistimsession *pte, char keycode)
 {
-	int fav;
-
-	if ((keycode < KEY_FAV1) && (keycode > KEY_FAV5)) {
-		ast_log(LOG_WARNING, "It's not a favorite key\n");
-		return;
-	}
-	if (keycode == KEY_FAV0)
-		return;
-	fav = keycode - KEY_FAV0;
-	if (pte->device->softkeyicon[fav] == 0)
-		return;
+        int fav = keycode - KEY_FAV0;
+        if (!is_key_favorite(pte->device, fav)) {
+                ast_log(LOG_WARNING, "It's not a favorite key\n");
+                return;
+        }
 	ast_copy_string(pte->device->phone_number, pte->device->softkeynumber[fav],
 					sizeof(pte->device->phone_number));
-	HandleCallOutgoing(pte);
+	handle_call_outgoing(pte);
 	return;
 }
 
 static void key_dial_page(struct unistimsession *pte, char keycode)
 {
+        struct ast_channel *chan = NULL;
+        struct unistim_subchannel *sub = get_sub(pte->device, SUB_REAL);
+
+        if (sub)
+                chan = sub->owner;
+
 	if (keycode == KEY_FUNC3) {
 		if (pte->device->size_phone_number <= 1)
 			keycode = KEY_FUNC4;
@@ -2702,50 +3205,32 @@
 			keycode = pte->device->phone_number[pte->device->size_phone_number] + 0x10;
 		}
 	}
+        if (keycode == KEY_SHARP && pte->device->sharp_dial == 1) {
+                keycode = KEY_FUNC1;
+        }
 	if ((keycode >= KEY_0) && (keycode <= KEY_SHARP)) {
-		char tmpbuf[] = "Number : ...............";
-		int i = 0;
+		int i = pte->device->size_phone_number;
 
-		if (pte->device->size_phone_number >= 15)
-			return;
 		if (pte->device->size_phone_number == 0)
 			send_tone(pte, 0, 0);
-		while (i < pte->device->size_phone_number) {
-			tmpbuf[i + 9] = pte->device->phone_number[i];
-			i++;
-		}
-		if (keycode == KEY_SHARP)
-			keycode = '#';
+
+		if (keycode == KEY_SHARP) 
+                        keycode = '#';
 		else if (keycode == KEY_STAR)
 			keycode = '*';
 		else
 			keycode -= 0x10;
-		tmpbuf[i + 9] = keycode;
 		pte->device->phone_number[i] = keycode;
 		pte->device->size_phone_number++;
 		pte->device->phone_number[i + 1] = 0;
-		if (pte->device->height == 1) {
-			send_text(TEXT_LINE0, TEXT_NORMAL, pte, tmpbuf);
-		} else {
-			send_text(TEXT_LINE2, TEXT_NORMAL, pte, tmpbuf);
-		}
-		send_blink_cursor(pte);
-		send_cursor_pos(pte, (unsigned char) (TEXT_LINE2 + 0x0a + i));
+                
+		show_phone_number(pte);
 		return;
 	}
 	if (keycode == KEY_FUNC4) {
-
 		pte->device->size_phone_number = 0;
-		if (pte->device->height == 1) {
-			send_text(TEXT_LINE0, TEXT_NORMAL, pte, "Number : ...............");
-			send_blink_cursor(pte);
-			send_cursor_pos(pte, TEXT_LINE0 + 0x09);
-		} else {
-			send_text(TEXT_LINE2, TEXT_NORMAL, pte, "Number : ...............");
-			send_blink_cursor(pte);
-			send_cursor_pos(pte, TEXT_LINE2 + 0x09);
-		}
-		return;
+                show_phone_number(pte);
+                return;
 	}
 
 	if (pte->device->call_forward[0] == -1) {
@@ -2755,7 +3240,9 @@
 			show_main_page(pte);
 		} else if ((keycode == KEY_FUNC2) || (keycode == KEY_HANGUP)) {
 			pte->device->call_forward[0] = '\0';
-			show_main_page(pte);
+			send_led_update(pte, 0x08);
+                        send_led_update(pte, 0x10);
+                        show_main_page(pte);
 		}
 		return;
 	}
@@ -2766,40 +3253,41 @@
 		ast_copy_string(pte->device->phone_number, pte->device->redial_number,
 						sizeof(pte->device->phone_number));
 	case KEY_FUNC1:
-		HandleCallOutgoing(pte);
+		handle_call_outgoing(pte);
 		break;
 	case KEY_HANGUP:
-		if (pte->device->lines->subs[SUB_REAL]->owner) {
-			/* Stop the silence generator */
-			if (pte->device->silence_generator) {
-				if (unistimdebug)
-					ast_verb(0, "Stopping silence generator\n");
-				ast_channel_stop_silence_generator(pte->device->lines->subs[SUB_REAL]->
-												   owner, pte->device->silence_generator);
-				pte->device->silence_generator = NULL;
-			}
-			send_tone(pte, 0, 0);
-			ast_moh_stop(ast_bridged_channel(pte->device->lines->subs[SUB_REAL]->owner));
-			pte->device->moh = 0;
+		if (chan) {
+                        struct ast_channel *bridgepeer = NULL;
+                        
+                        sub_stop_silence(pte, sub);
+                        send_tone(pte, 0, 0);
+                        if ((bridgepeer = ast_bridged_channel(chan))) {
+                                ast_moh_stop(bridgepeer);
+                        }
+			get_sub(pte->device, SUB_REAL)->moh = 0;
 			pte->state = STATE_CALL;
 
 			if (pte->device->height == 1) {
-				send_text(TEXT_LINE0, TEXT_NORMAL, pte, "Dial Cancel,back to priv. call.");
+				send_text(TEXT_LINE0, TEXT_NORMAL, pte, ustmtext("Dial Cancel,back to priv. call.", pte));
 			} else {
-				send_text(TEXT_LINE0, TEXT_NORMAL, pte, "Dialing canceled,");
-				send_text(TEXT_LINE1, TEXT_NORMAL, pte, "switching back to");
-				send_text(TEXT_LINE2, TEXT_NORMAL, pte, "previous call.");
+				send_text(TEXT_LINE0, TEXT_NORMAL, pte, ustmtext("Dialing canceled,", pte));
+				send_text(TEXT_LINE1, TEXT_NORMAL, pte, ustmtext("switching back to", pte));
+				send_text(TEXT_LINE2, TEXT_NORMAL, pte, ustmtext("previous call.", pte));
 			}
-			send_text_status(pte, "Hangup Transf");
-		} else
+			send_text_status(pte, ustmtext("Hangup Transf", pte));
+		} else {                        
+			send_led_update(pte, 0x08);
+                        send_led_update(pte, 0x10);
 			show_main_page(pte);
+                }
 		break;
-	case KEY_FAV1:
+	case KEY_FAV0:
+        case KEY_FAV1:
 	case KEY_FAV2:
 	case KEY_FAV3:
 	case KEY_FAV4:
 	case KEY_FAV5:
-		Keyfavorite(pte, keycode);
+                handle_key_fav(pte, keycode);
 		break;
 	case KEY_LOUDSPK:
 		if (pte->device->output == OUTPUT_SPEAKER) {
@@ -2824,10 +3312,50 @@
 	return;
 }
 
+static void handle_select_option(struct unistimsession *pte)
+{
+        char tmp[128];
+
+        if (pte->state != STATE_SELECTOPTION) {
+                pte->state = STATE_SELECTOPTION;
+                pte->size_buff_entry = 1;
+                pte->buff_entry[0] = 0; /* Position in menu */
+        }
+        sprintf(tmp, "%d. %s", pte->buff_entry[0] + 1, ustmtext(options_menu[(int)pte->buff_entry[0]].label, pte));
+	send_text(TEXT_LINE0, TEXT_NORMAL, pte, tmp);
+	send_text_status(pte, ustmtext("Select               Cancel", pte));
+	return;
+}
+
+static void key_select_option(struct unistimsession *pte, char keycode)
+{
+	switch (keycode) {
+        case KEY_DOWN:
+                pte->buff_entry[0]++;
+                if (options_menu[(int)pte->buff_entry[0]].label == NULL)
+                        pte->buff_entry[0]--;
+                break;
+        case KEY_UP:
+                if (pte->buff_entry[0] > 0)
+                        pte->buff_entry[0]--;
+                break;
+	case KEY_FUNC1:
+		options_menu[(int)pte->buff_entry[0]].handle_option(pte);
+		return;
+	case KEY_HANGUP:
+	case KEY_FUNC4:
+		show_main_page(pte);
+		return;
+        }
+
+        handle_select_option(pte);
+	return;
+}
+
 #define SELECTCODEC_START_ENTRY_POS 15
 #define SELECTCODEC_MAX_LENGTH 2
 #define SELECTCODEC_MSG "Codec number : .."
-static void HandleSelectCodec(struct unistimsession *pte)
+static void handle_select_codec(struct unistimsession *pte)
 {
 	char buf[30], buf2[5];
 
@@ -2843,7 +3371,7 @@
 	send_blink_cursor(pte);
 	send_cursor_pos(pte, TEXT_LINE2 + SELECTCODEC_START_ENTRY_POS);
 	pte->size_buff_entry = 0;
-	send_text_status(pte, "Select BackSpcErase  Cancel");
+	send_text_status(pte, ustmtext("Select BackSp Erase  Cancel", pte));
 	return;
 }
 
@@ -2901,6 +3429,98 @@
 	return;
 }
 
+static void handle_select_contrast(struct unistimsession *pte)
+{
+        return;
+}
+
+static void key_select_contrast(struct unistimsession *pte, char keycode)
+{
+	switch (keycode) {
+        case KEY_DOWN:
+                pte->buff_entry[0]++;
+                break;
+        case KEY_UP:
+                if (pte->buff_entry[0] > 0)
+                        pte->buff_entry[0]--;
+                break;
+	case KEY_FUNC1:
+		// Contrast saved
+                show_main_page(pte);
+		return;
+	case KEY_HANGUP:
+	case KEY_FUNC4:
+		handle_select_option(pte);
+		return;
+        }
+
+        handle_select_contrast(pte);
+	return;
+}
+
+static int find_language(const char* lang)
+{
+        int i = 0;
+        while (options_languages[i].lang_short != NULL) {
+                if(!strcmp(options_languages[i].lang_short, lang))
+                        return i;
+                i++;
+        }
+        return 0;
+}
+
+static void handle_select_language(struct unistimsession *pte)
+{
+        char tmp_language[40];
+        struct unistim_languages lang;
+
+        if (pte->state != STATE_SELECTLANGUAGE) {
+                pte->state = STATE_SELECTLANGUAGE;
+                pte->size_buff_entry = 1;
+                pte->buff_entry[0] = find_language(pte->device->language);
+        }
+        lang = options_languages[(int)pte->buff_entry[0]];
+        strcpy(tmp_language, pte->device->language);
+        strcpy(pte->device->language, lang.lang_short);
+        send_charset_update(pte, lang.encoding);
+	send_text(TEXT_LINE0, TEXT_NORMAL, pte, ustmtext(lang.label, pte));
+
+	strcpy(pte->device->language, tmp_language);
+        lang = options_languages[find_language(pte->device->language)];
+        send_charset_update(pte, lang.encoding);
+        send_text_status(pte, ustmtext("Select               Cancel", pte));
+	return;
+}
+
+static void key_select_language(struct unistimsession *pte, char keycode)
+{
+	switch (keycode) {
+        case KEY_DOWN:
+                pte->buff_entry[0]++;
+                if (options_languages[(int)pte->buff_entry[0]].label == NULL)
+                        pte->buff_entry[0]--;
+                break;
+        case KEY_UP:
+                if (pte->buff_entry[0] > 0)
+                        pte->buff_entry[0]--;
+                break;
+	case KEY_FUNC1:
+                ast_copy_string(pte->device->language, options_languages[(int)pte->buff_entry[0]].lang_short, sizeof(pte->device->language));
+                send_charset_update(pte, options_languages[(int)pte->buff_entry[0]].encoding);
+                refresh_all_favorite(pte);
+                show_main_page(pte);
+		return;
+	case KEY_HANGUP:
+	case KEY_FUNC4:
+		handle_select_option(pte);
+		return;
+        }
+
+        handle_select_language(pte);
+	return;
+}
+
+
 #define SELECTEXTENSION_START_ENTRY_POS 0
 #define SELECTEXTENSION_MAX_LENGTH 10
 #define SELECTEXTENSION_MSG ".........."
@@ -2908,12 +3528,12 @@
 {
 	pte->state = STATE_EXTENSION;
 
-	send_text(TEXT_LINE0, TEXT_NORMAL, pte, "Please enter a Terminal");
-	send_text(TEXT_LINE1, TEXT_NORMAL, pte, "Number (TN) :");
+	send_text(TEXT_LINE0, TEXT_NORMAL, pte, ustmtext("Please enter a Terminal", pte));
+	send_text(TEXT_LINE1, TEXT_NORMAL, pte, ustmtext("Number (TN) :", pte));
 	send_text(TEXT_LINE2, TEXT_NORMAL, pte, SELECTEXTENSION_MSG);
 	send_blink_cursor(pte);
 	send_cursor_pos(pte, TEXT_LINE2 + SELECTEXTENSION_START_ENTRY_POS);
-	send_text_status(pte, "Enter  BackSpcErase");
+	send_text_status(pte, ustmtext("Enter  BackSpcErase", pte));
 	pte->size_buff_entry = 0;
 	return;
 }
@@ -2985,8 +3605,8 @@
 				d = d->next;
 			}
 			ast_mutex_unlock(&devicelock);
-			send_text(TEXT_LINE0, TEXT_NORMAL, pte, "Invalid Terminal Number.");
-			send_text(TEXT_LINE1, TEXT_NORMAL, pte, "Please try again :");
+			send_text(TEXT_LINE0, TEXT_NORMAL, pte, ustmtext("Invalid Terminal Number.", pte));
+			send_text(TEXT_LINE1, TEXT_NORMAL, pte, ustmtext("Please try again :", pte));
 			send_cursor_pos(pte,
 						  (unsigned char) (TEXT_LINE2 + SELECTEXTENSION_START_ENTRY_POS +
 										   pte->size_buff_entry));
@@ -2994,9 +3614,9 @@
 		} else {
 			ast_copy_string(pte->device->extension_number, pte->buff_entry,
 							pte->size_buff_entry + 1);
-			if (RegisterExtension(pte)) {
-				send_text(TEXT_LINE0, TEXT_NORMAL, pte, "Invalid extension.");
-				send_text(TEXT_LINE1, TEXT_NORMAL, pte, "Please try again :");
+			if (register_extension(pte)) {
+				send_text(TEXT_LINE0, TEXT_NORMAL, pte, ustmtext("Invalid extension.", pte));
+				send_text(TEXT_LINE1, TEXT_NORMAL, pte, ustmtext("Please try again :", pte));
 				send_cursor_pos(pte,
 							  (unsigned char) (TEXT_LINE2 +
 											   SELECTEXTENSION_START_ENTRY_POS +
@@ -3045,22 +3665,37 @@
 		return;
 	}
 	line[sizeof(line) - 1] = '\0';
-	send_text(TEXT_LINE0, TEXT_NORMAL, pte, line);
-	if (fread(line, TEXT_LENGTH_MAX, 1, *f) != 1) {
+        if (pte->device->height == 1) {
+                if (pte->buff_entry[3] == 1)
+                        send_text(TEXT_LINE0, TEXT_NORMAL, pte, line);
+        } else {
+                send_text(TEXT_LINE0, TEXT_NORMAL, pte, line);
+        }
+        if (fread(line, TEXT_LENGTH_MAX, 1, *f) != 1) {
 		display_last_error("Can't read callerid entry");
 		fclose(*f);
 		return;
 	}
 	line[sizeof(line) - 1] = '\0';
 	ast_copy_string(pte->device->lst_cid, line, sizeof(pte->device->lst_cid));
-	send_text(TEXT_LINE1, TEXT_NORMAL, pte, line);
-	if (fread(line, TEXT_LENGTH_MAX, 1, *f) != 1) {
+        if (pte->device->height == 1) {
+                if (pte->buff_entry[3] == 2)
+                        send_text(TEXT_LINE0, TEXT_NORMAL, pte, line);
+        } else {
+                send_text(TEXT_LINE1, TEXT_NORMAL, pte, line);
+        }
+        if (fread(line, TEXT_LENGTH_MAX, 1, *f) != 1) {
 		display_last_error("Can't read callername entry");
 		fclose(*f);
 		return;
 	}
 	line[sizeof(line) - 1] = '\0';
-	send_text(TEXT_LINE2, TEXT_NORMAL, pte, line);
+                if (pte->device->height == 1) {
+                if (pte->buff_entry[3] == 3)
+                        send_text(TEXT_LINE0, TEXT_NORMAL, pte, line);
+        } else {
+                send_text(TEXT_LINE2, TEXT_NORMAL, pte, line);
+        }
 	fclose(*f);
 
 	snprintf(line, sizeof(line), "Call %03d/%03d", pte->buff_entry[2],
@@ -3070,16 +3705,16 @@
 	if (pte->buff_entry[2] == 1)
 		strcpy(func1, "       ");
 	else
-		strcpy(func1, "Prvious");
+		strcpy(func1, ustmtext("Prvious", pte));
 	if (pte->buff_entry[2] >= pte->buff_entry[1])
 		strcpy(func2, "       ");
 	else
-		strcpy(func2, "Next   ");
+		strcpy(func2, ustmtext("Next   ", pte));
 	if (ReformatNumber(pte->device->lst_cid))
-		strcpy(func3, "Redial ");
+		strcpy(func3, ustmtext("Redial ", pte));
 	else
 		strcpy(func3, "       ");
-	snprintf(status, sizeof(status), "%s%s%sCancel", func1, func2, func3);
+	snprintf(status, sizeof(status), ustmtext("%s%s%sCancel", pte), func1, func2, func3);
 	send_text_status(pte, status);
 }
 
@@ -3124,6 +3759,7 @@
 	pte->buff_entry[0] = way;
 	pte->buff_entry[1] = count;
 	pte->buff_entry[2] = 1;
+        pte->buff_entry[3] = 1;
 	show_entry_history(pte, &f);
 	pte->state = STATE_HISTORY;
 }
@@ -3132,7 +3768,6 @@
 {
 	char tmpbuf[TEXT_LENGTH_MAX + 1];
 
-
 	if ((pte->device->extension == EXTENSION_ASK) &&
 		(ast_strlen_zero(pte->device->extension_number))) {
 		ShowExtensionPage(pte);
@@ -3143,40 +3778,52 @@
 
 	send_tone(pte, 0, 0);
 	send_select_output(pte, pte->device->output, pte->device->volume, MUTE_ON_DISCRET);
-	pte->device->lines->lastmsgssent = 0;
-	send_favorite(pte->device->softkeylinepos, FAV_ICON_ONHOOK_BLACK, pte,
-				 pte->device->softkeylabel[pte->device->softkeylinepos]);
+	send_led_update(pte, 0x08);
+        send_led_update(pte, 0x10);
+	get_main_line(pte->device)->lastmsgssent = 0;
+	//send_favorite(softkeylinepos, FAV_ICON_ONHOOK_BLACK, pte,
+	//			 pte->device->softkeylabel[softkeylinepos]);
 	if (!ast_strlen_zero(pte->device->call_forward)) {
 		if (pte->device->height == 1) {
-			send_text(TEXT_LINE0, TEXT_NORMAL, pte, "Forwarding ON");
+                        char tmp_field[100];
+                        sprintf(tmp_field, ustmtext("Fwd to: %s", pte), pte->device->call_forward);
+			send_text(TEXT_LINE0, TEXT_NORMAL, pte, tmp_field);
 		} else {
-			send_text(TEXT_LINE0, TEXT_NORMAL, pte, "Call forwarded to :");
+			send_text(TEXT_LINE0, TEXT_NORMAL, pte, ustmtext("Call forwarded to :", pte));
 			send_text(TEXT_LINE1, TEXT_NORMAL, pte, pte->device->call_forward);
 		}
 		Sendicon(TEXT_LINE0, FAV_ICON_REFLECT + FAV_BLINK_SLOW, pte);
-		send_text_status(pte, "Dial   Redial NoForwd");
+		send_text_status(pte, ustmtext("Dial   Redial NoFwd  ", pte));
 	} else {
 		if ((pte->device->extension == EXTENSION_ASK) ||
 			(pte->device->extension == EXTENSION_TN))
-			send_text_status(pte, "Dial   Redial ForwardUnregis");
+			send_text_status(pte, ustmtext("Dial   Recal  Fwd    Unregis", pte));
 		else
-			send_text_status(pte, "Dial   Redial Forward");
+			send_text_status(pte, ustmtext("Dial   Recal  Fwd    Pickup", pte));
 
 		send_text(TEXT_LINE1, TEXT_NORMAL, pte, pte->device->maintext1);
-		if (pte->device->missed_call == 0)
-			send_text(TEXT_LINE0, TEXT_NORMAL, pte, pte->device->maintext0);
-		else {
-			sprintf(tmpbuf, "%d unanswered call(s)", pte->device->missed_call);
-			send_text(TEXT_LINE0, TEXT_NORMAL, pte, tmpbuf);
+		if (pte->device->missed_call == 0) {
+                        send_date_time2(pte);
+                        send_idle_clock(pte);
+			//send_text(TEXT_LINE0, TEXT_NORMAL, pte, pte->device->maintext0);
+                } else {
+                        if (pte->device->missed_call == 1)
+                                sprintf(tmpbuf, ustmtext("%d unanswered call", pte), pte->device->missed_call);
+                        else
+                                sprintf(tmpbuf, ustmtext("%d unanswered calls", pte), pte->device->missed_call);
+                        send_text(TEXT_LINE0, TEXT_NORMAL, pte, tmpbuf);
 			Sendicon(TEXT_LINE0, FAV_ICON_CALL_CENTER + FAV_BLINK_SLOW, pte);
 		}
 	}
-	if (ast_strlen_zero(pte->device->maintext2)) {
-		strcpy(tmpbuf, "IP : ");
-		strcat(tmpbuf, ast_inet_ntoa(pte->sin.sin_addr));
-		send_text(TEXT_LINE2, TEXT_NORMAL, pte, tmpbuf);
-	} else
-		send_text(TEXT_LINE2, TEXT_NORMAL, pte, pte->device->maintext2);
+        if (pte->device->height > 1) {
+                if (ast_strlen_zero(pte->device->maintext2)) {
+                        strcpy(tmpbuf, "IP : ");
+                        strcat(tmpbuf, ast_inet_ntoa(pte->sin.sin_addr));
+                        send_text(TEXT_LINE2, TEXT_NORMAL, pte, tmpbuf);
+                } else
+                        send_text(TEXT_LINE2, TEXT_NORMAL, pte, pte->device->maintext2);
+        }
+
 	send_texttitle(pte, pte->device->titledefault);
 	change_favorite_icon(pte, FAV_ICON_ONHOOK_BLACK);
 }
@@ -3194,6 +3841,7 @@
 	}
 	switch (keycode) {
 	case KEY_FUNC1:
+                pte->device->selected = get_avail_softkey(pte);
 		handle_dial_page(pte);
 		break;
 	case KEY_FUNC2:
@@ -3207,7 +3855,7 @@
 
 		ast_copy_string(pte->device->phone_number, pte->device->redial_number,
 						sizeof(pte->device->phone_number));
-		HandleCallOutgoing(pte);
+		handle_call_outgoing(pte);
 		break;
 	case KEY_FUNC3:
 		if (!ast_strlen_zero(pte->device->call_forward)) {
@@ -3237,25 +3885,23 @@
 			pte->device = NULL;
 			ast_mutex_unlock(&devicelock);
 			ShowExtensionPage(pte);
-		}
+		} else { /* Pickup function */
+                    	ast_copy_string(pte->device->phone_number, ast_pickup_ext(),
+						sizeof(pte->device->phone_number));
+                        handle_call_outgoing(pte);
+
+                }
 		break;
-	case KEY_FAV0:
-		handle_dial_page(pte);
-		break;
+        case KEY_FAV0: 
 	case KEY_FAV1:
 	case KEY_FAV2:
 	case KEY_FAV3:
 	case KEY_FAV4:
 	case KEY_FAV5:
-		if ((pte->device->output == OUTPUT_HANDSET) &&
-			(pte->device->receiver_state == STATE_ONHOOK))
-			send_select_output(pte, OUTPUT_SPEAKER, pte->device->volume, MUTE_OFF);
-		else
-			send_select_output(pte, pte->device->output, pte->device->volume, MUTE_OFF);
-		Keyfavorite(pte, keycode);
+                handle_key_fav(pte, keycode);
 		break;
 	case KEY_CONF:
-		HandleSelectCodec(pte);
+		handle_select_option(pte);
 		break;
 	case KEY_LOUDSPK:
 		send_select_output(pte, OUTPUT_SPEAKER, pte->device->volume, MUTE_OFF);
@@ -3280,41 +3926,38 @@
 	FILE *f;
 	char count;
 	long offset;
+        int flag = 0;
 
 	switch (keycode) {
+        case KEY_LEFT:
+                if (pte->device->height == 1) {
+                        if (pte->buff_entry[3] <= 1)
+                                return;
+                        pte->buff_entry[3]--;
+                        flag = 1;
+                        break;
+                }
 	case KEY_UP:
-	case KEY_LEFT:
 	case KEY_FUNC1:
 		if (pte->buff_entry[2] <= 1)
 			return;
 		pte->buff_entry[2]--;
-		count = OpenHistory(pte, pte->buff_entry[0], &f);
-		if (!count)
-			return;
-		offset = ((pte->buff_entry[2] - 1) * TEXT_LENGTH_MAX * 3);
-		if (fseek(f, offset, SEEK_CUR)) {
-			display_last_error("Unable to seek history entry.");
-			fclose(f);
-			return;
-		}
-		show_entry_history(pte, &f);
+                flag = 1;
 		break;
-	case KEY_DOWN:
-	case KEY_RIGHT:
+        case KEY_RIGHT:
+                if (pte->device->height == 1) {
+                        if (pte->buff_entry[3] == 3)
+                                return;
+                        pte->buff_entry[3]++;
+                        flag = 1;
+                        break;
+                }
+        case KEY_DOWN:
 	case KEY_FUNC2:
 		if (pte->buff_entry[2] >= pte->buff_entry[1])
 			return;
 		pte->buff_entry[2]++;
-		count = OpenHistory(pte, pte->buff_entry[0], &f);
-		if (!count)
-			return;
-		offset = ((pte->buff_entry[2] - 1) * TEXT_LENGTH_MAX * 3);
-		if (fseek(f, offset, SEEK_CUR)) {
-			display_last_error("Unable to seek history entry.");
-			fclose(f);
-			return;
-		}
-		show_entry_history(pte, &f);
+                flag = 1;
 		break;
 	case KEY_FUNC3:
 		if (!ReformatNumber(pte->device->lst_cid))
@@ -3340,6 +3983,20 @@
 			show_history(pte, 'i');
 		break;
 	}
+
+        if (flag) {
+		count = OpenHistory(pte, pte->buff_entry[0], &f);
+		if (!count)
+			return;
+		offset = ((pte->buff_entry[2] - 1) * TEXT_LENGTH_MAX * 3);
+		if (fseek(f, offset, SEEK_CUR)) {
+			display_last_error("Unable to seek history entry.");
+			fclose(f);
+			return;
+		}
+		show_entry_history(pte, &f);
+        }
+
 	return;
 }
 
@@ -3350,7 +4007,7 @@
 		ast_verb(0, "Sending S4\n");
 	memcpy(buffsend + SIZE_HEADER, packet_send_s4, sizeof(packet_send_s4));
 	send_client(SIZE_HEADER + sizeof(packet_send_s4), buffsend, pte);
-	send_date_time2(pte);
+        send_date_time2(pte);
 	send_date_time3(pte);
 	if (unistimdebug)
 		ast_verb(0, "Sending S7\n");
@@ -3375,7 +4032,11 @@
 	send_client(SIZE_HEADER + sizeof(packet_send_S7), buffsend, pte);
 	send_led_update(pte, 0);
 	send_ping(pte);
-	if (pte->state < STATE_MAINPAGE) {
+	if (unistimdebug)
+		ast_verb(0, "Sending init language\n");
+        send_charset_update(pte, options_languages[find_language(pte->device->language)].encoding);
+
+        if (pte->state < STATE_MAINPAGE) {
 		if (autoprovisioning == AUTOPROVISIONING_TN) {
 			ShowExtensionPage(pte);
 			return;
@@ -3385,8 +4046,8 @@
 
 			for (i = 1; i < 6; i++)
 				send_favorite(i, 0, pte, "");
-			send_text(TEXT_LINE0, TEXT_NORMAL, pte, "Sorry, this phone is not");
-			send_text(TEXT_LINE1, TEXT_NORMAL, pte, "registered in unistim.cfg");
+			send_text(TEXT_LINE0, TEXT_NORMAL, pte, ustmtext("Sorry, this phone is not", pte));
+			send_text(TEXT_LINE1, TEXT_NORMAL, pte, ustmtext("registered in unistim.cfg", pte));
 			strcpy(tmp, "MAC = ");
 			strcat(tmp, pte->macaddr);
 			send_text(TEXT_LINE2, TEXT_NORMAL, pte, tmp);
@@ -3467,10 +4128,19 @@
 		case STATE_EXTENSION:
 			key_select_extension(pte, keycode);
 			break;
+                case STATE_SELECTOPTION:
+                        key_select_option(pte, keycode);
+                        break;
 		case STATE_SELECTCODEC:
 			key_select_codec(pte, keycode);
 			break;
-		case STATE_HISTORY:
+                case STATE_SELECTCONTRAST:
+                        key_select_contrast(pte, keycode);
+                        break;
+                case STATE_SELECTLANGUAGE:
+                        key_select_language(pte, keycode);
+                        break;
+                case STATE_HISTORY:
 			key_history(pte, keycode);
 			break;
 		default:
@@ -3488,13 +4158,18 @@
 			send_select_output(pte, OUTPUT_HEADPHONE, pte->device->volume, MUTE_OFF);
 		else
 			send_select_output(pte, OUTPUT_HANDSET, pte->device->volume, MUTE_OFF);
-		if (pte->state == STATE_RINGING)
-			HandleCallIncoming(pte);
-		else if ((pte->state == STATE_DIALPAGE) || (pte->state == STATE_CALL))
+		if (pte->state == STATE_RINGING) {
+                        /* pickup latest ringing line
+                         or pickup main_line, then goup line
+                         */
+			handle_call_incoming(pte);
+
+                } else if ((pte->state == STATE_DIALPAGE) || (pte->state == STATE_CALL))
 			send_select_output(pte, OUTPUT_HANDSET, pte->device->volume, MUTE_OFF);
 		else if (pte->state == STATE_EXTENSION) /* We must have a TN before calling */
 			return;
 		else {
+                        pte->device->selected = get_avail_softkey(pte);
 			send_select_output(pte, OUTPUT_HANDSET, pte->device->volume, MUTE_OFF);
 			handle_dial_page(pte);
 		}
@@ -3508,7 +4183,7 @@
 		pte->device->receiver_state = STATE_ONHOOK;
 		if (pte->state == STATE_CALL)
 			close_call(pte);
-		else if (pte->device->lines->subs[SUB_REAL]->owner)
+                else if (get_sub(pte->device, SUB_REAL) && get_sub(pte->device, SUB_REAL)->owner)
 			close_call(pte);
 		else if (pte->state == STATE_EXTENSION)
 			return;
@@ -3706,6 +4381,46 @@
 	return sub->parent->parent->session;
 }
 
+static void send_callerid_screen(struct unistim_subchannel *sub) {
+        struct unistimsession *session;
+        char *cidname_str;
+        char *cidnum_str;
+
+        if (!sub)
+                return;
+        session = sub->parent->parent->session;
+	if (sub->owner) {
+		if (sub->owner->connected.id.number.valid && sub->owner->connected.id.number.str) {
+                        cidnum_str = sub->owner->connected.id.number.str;
+		} else {
+                        cidnum_str = DEFAULTCALLERID;
+		}
+                change_callerid(session, 0, cidnum_str);
+                if (strlen(cidnum_str) == 0) {
+                        cidnum_str = DEFAULTCALLERID;
+                }
+
+		if (sub->owner->connected.id.name.valid && sub->owner->connected.id.name.str) {
+			cidname_str = sub->owner->connected.id.name.str;
+		} else {
+			cidname_str = DEFAULTCALLERNAME;
+		}
+		change_callerid(session, 1, cidname_str);
+                if (strlen(cidname_str) == 0) {
+                        cidname_str = DEFAULTCALLERNAME;
+                }
+
+                if (session->device->height == 1) {
+                        char tmpstr[256];
+                        sprintf(tmpstr, "%s %s", cidnum_str, cidname_str);
+                        send_text(TEXT_LINE0, TEXT_NORMAL, session, tmpstr);
+                } else {
+                        send_text(TEXT_LINE0, TEXT_NORMAL, session, cidname_str);
+                        send_text(TEXT_LINE1, TEXT_NORMAL, session, cidnum_str);
+                }
+	}
+}
+
 /*--- unistim_call: Initiate UNISTIM call from PBX ---*/
 /*      used from the dial() application      */
 static int unistim_call(struct ast_channel *ast, char *dest, int timeout)
@@ -3733,48 +4448,26 @@
 	session->state = STATE_RINGING;
 	Sendicon(TEXT_LINE0, FAV_ICON_NONE, session);
 
-	if (sub->owner) {
-		if (sub->owner->connected.id.number.valid
-			&& sub->owner->connected.id.number.str) {
-			if (session->device->height == 1) {
-				send_text(TEXT_LINE0, TEXT_NORMAL, session, sub->owner->connected.id.number.str);
-			} else {
-				send_text(TEXT_LINE1, TEXT_NORMAL, session, sub->owner->connected.id.number.str);
-			}
-			change_callerid(session, 0, sub->owner->connected.id.number.str);
-		} else {
-			if (session->device->height == 1) {
-				send_text(TEXT_LINE0, TEXT_NORMAL, session, DEFAULTCALLERID);
-			} else {
-				send_text(TEXT_LINE1, TEXT_NORMAL, session, DEFAULTCALLERID);
-			}
-			change_callerid(session, 0, DEFAULTCALLERID);
-		}
-		if (sub->owner->connected.id.name.valid
-			&& sub->owner->connected.id.name.str) {
-			send_text(TEXT_LINE0, TEXT_NORMAL, session, sub->owner->connected.id.name.str);
-			change_callerid(session, 1, sub->owner->connected.id.name.str);
-		} else {
-			send_text(TEXT_LINE0, TEXT_NORMAL, session, DEFAULTCALLERNAME);
-			change_callerid(session, 1, DEFAULTCALLERNAME);
-		}
-	}
-	send_text(TEXT_LINE2, TEXT_NORMAL, session, "is calling you.");
-	send_text_status(session, "Accept              Ignore");
+        send_callerid_screen(sub);
+        if (!strlen(ast->call_forward)) { /* Send ring only if no call forward, otherwise short ring will apear */
+                send_text(TEXT_LINE2, TEXT_NORMAL, session, ustmtext("is calling you.", session));
+                send_text_status(session, ustmtext("Accept               Ignore", session));
 
-	if (sub->ringstyle == -1)
-		send_ring(session, session->device->ringvolume, session->device->ringstyle);
-	else {
-		if (sub->ringvolume == -1)
-			send_ring(session, session->device->ringvolume, sub->ringstyle);
-		else
-			send_ring(session, sub->ringvolume, sub->ringstyle);
-	}
-	change_favorite_icon(session, FAV_ICON_SPEAKER_ONHOOK_BLACK + FAV_BLINK_FAST);
+                if (sub->ringstyle == -1)
+                        send_ring(session, session->device->ringvolume, session->device->ringstyle);
+                else {
+                        if (sub->ringvolume == -1)
+                                send_ring(session, session->device->ringvolume, sub->ringstyle);
+                        else
+                                send_ring(session, sub->ringvolume, sub->ringstyle);
+                }
 
-	ast_setstate(ast, AST_STATE_RINGING);
-	ast_queue_control(ast, AST_CONTROL_RINGING);
-	return res;
+                send_favorite_short(sub->softkey, FAV_ICON_SPEAKER_ONHOOK_BLACK + FAV_BLINK_FAST, session);
+                change_favorite_icon(session, FAV_ICON_SPEAKER_ONHOOK_BLACK + FAV_BLINK_FAST);
+        }
+        ast_setstate(ast, AST_STATE_RINGING);
+        ast_queue_control(ast, AST_CONTROL_RINGING);
+        return res;
 }
 
 /*--- unistim_hangup: Hangup UNISTIM call */
@@ -3782,7 +4475,9 @@
 {
 	struct unistim_subchannel *sub;
 	struct unistim_line *l;
-	struct unistimsession *s;
+	struct unistim_device *d;
+        struct unistimsession *s;
+        int i;
 
 	s = channel_to_session(ast);
 	sub = ast->tech_pvt;
@@ -3802,32 +4497,33 @@
 		return 0;
 	}
 	l = sub->parent;
+        d = l->parent;
 	if (unistimdebug)
 		ast_verb(0, "unistim_hangup(%s) on %s@%s\n", ast->name, l->name, l->parent->name);
-
-	if ((l->subs[SUB_THREEWAY]) && (sub->subtype == SUB_REAL)) {
+	if ((get_sub(d, SUB_THREEWAY)) && (sub->subtype == SUB_REAL)) {
 		if (unistimdebug)
 			ast_verb(0, "Real call disconnected while talking to threeway\n");
 		sub->owner = NULL;
 		ast->tech_pvt = NULL;
 		return 0;
 	}
-	if ((l->subs[SUB_REAL]->owner) && (sub->subtype == SUB_THREEWAY) &&
+	if (get_sub(d, SUB_REAL) && (get_sub(d, SUB_REAL)->owner) && (sub->subtype == SUB_THREEWAY) &&
 		(sub->alreadygone == 0)) {
 		if (unistimdebug)
 			ast_verb(0, "threeway call disconnected, switching to real call\n");
-		send_text(TEXT_LINE0, TEXT_NORMAL, s, "Three way call canceled,");
-		send_text(TEXT_LINE1, TEXT_NORMAL, s, "switching back to");
-		send_text(TEXT_LINE2, TEXT_NORMAL, s, "previous call.");
-		send_text_status(s, "Hangup Transf");
-		ast_moh_stop(ast_bridged_channel(l->subs[SUB_REAL]->owner));
-		swap_subs(l, SUB_THREEWAY, SUB_REAL);
-		l->parent->moh = 0;
+		send_text(TEXT_LINE0, TEXT_NORMAL, s, ustmtext("Three way call canceled,", s));
+		send_text(TEXT_LINE1, TEXT_NORMAL, s, ustmtext("switching back to", s));
+		send_text(TEXT_LINE2, TEXT_NORMAL, s, ustmtext("previous call.", s));
+		send_text_status(s, ustmtext("Hangup Transf", s));
+		ast_moh_stop(ast_bridged_channel(get_sub(d, SUB_REAL)->owner));
+		get_sub(d, SUB_REAL)->moh = 0;
+                //swap_subs(d, SUB_THREEWAY, SUB_REAL);
+		
 		ast_mutex_lock(&sub->lock);
 		sub->owner = NULL;
 		ast->tech_pvt = NULL;
 		ast_mutex_unlock(&sub->lock);
-		unalloc_sub(l, SUB_THREEWAY);
+		unalloc_sub(d, sub);
 		return 0;
 	}
 	ast_mutex_lock(&sub->lock);
@@ -3847,22 +4543,12 @@
 		return 0;
 	}
 	if (sub->subtype == SUB_REAL) {
-		/* Stop the silence generator */
-		if (s->device->silence_generator) {
-			if (unistimdebug)
-				ast_verb(0, "Stopping silence generator\n");
-			if (sub->owner)
-				ast_channel_stop_silence_generator(sub->owner,
-												   s->device->silence_generator);
-			else
-				ast_log(LOG_WARNING,
-						"Trying to stop silence generator on a null channel !\n");
-			s->device->silence_generator = NULL;
-		}
-	}
-	l->parent->moh = 0;
+                sub_stop_silence(s, sub);
+        }
+	sub->moh = 0;
 	send_no_ring(s);
 	send_end_call(s);
+        send_favorite_short(sub->softkey, FAV_ICON_SPEAKER_ONHOOK_BLACK, s);
 	if (sub->rtp) {
 		if (unistimdebug)
 			ast_verb(0, "Destroying RTP session\n");
@@ -3870,15 +4556,27 @@
 		sub->rtp = NULL;
 	} else if (unistimdebug)
 		ast_verb(0, "No RTP session to destroy\n");
-	if (l->subs[SUB_THREEWAY]) {
+        /* Delete assign sub to softkey */
+        sub->softkey = -1;
+        for (i = 0; i < FAVNUM; i++)
+                if (s->device->ssub[i] == sub) {
+                        s->device->ssub[i] = NULL;
+                        break;
+                }
+
+	if (get_sub(d, SUB_THREEWAY)) {
 		if (unistimdebug)
 			ast_verb(0, "Cleaning other subchannels\n");
-		unalloc_sub(l, SUB_THREEWAY);
+		unalloc_sub(d, get_sub(d, SUB_THREEWAY));
 	}
-	if (s->state == STATE_RINGING)
-		cancel_dial(s);
-	else if (s->state == STATE_CALL)
-		close_call(s);
+        switch (s->state) {
+            case STATE_RINGING:
+                cancel_dial(s);
+                break;
+            case STATE_CALL:
+                close_call(s);
+        }
+        unalloc_sub(s->device, sub);
 
 	return 0;
 }
@@ -3889,6 +4587,7 @@
 	int res = 0;
 	struct unistim_subchannel *sub;
 	struct unistim_line *l;
+        struct unistim_device *d;
 	struct unistimsession *s;
 
 	s = channel_to_session(ast);
@@ -3898,17 +4597,18 @@
 	}
 	sub = ast->tech_pvt;
 	l = sub->parent;
+        d = l->parent;
 
-	if ((!sub->rtp) && (!l->subs[SUB_THREEWAY]))
+	if ((!sub->rtp) && (!get_sub(d, SUB_THREEWAY)))
 		start_rtp(sub);
 	if (unistimdebug)
 		ast_verb(0, "unistim_answer(%s) on %s@%s-%d\n", ast->name, l->name,
 					l->parent->name, sub->subtype);
-	send_text(TEXT_LINE2, TEXT_NORMAL, l->parent->session, "is now on-line");
-	if (l->subs[SUB_THREEWAY])
-		send_text_status(l->parent->session, "Transf Cancel");
+	send_text(TEXT_LINE2, TEXT_NORMAL, l->parent->session, ustmtext("is now on-line", l->parent->session));
+	if (get_sub(d, SUB_THREEWAY))
+		send_text_status(l->parent->session, ustmtext("Transf Cancel", l->parent->session));
 	else
-		send_text_status(l->parent->session, "Hangup Transf");
+		send_text_status(l->parent->session, ustmtext("Hangup Transf", l->parent->session));
 	send_start_timer(l->parent->session);
 	if (ast->_state != AST_STATE_UP)
 		ast_setstate(ast, AST_STATE_UP);
@@ -4047,14 +4747,16 @@
 			return 0;
 		}
 	} else {
-		if (!(frame->subclass.codec & ast->nativeformats)) {
+                if (!(frame->subclass.codec & ast->nativeformats)) {
 			char tmp[256];
 			ast_log(LOG_WARNING,
-					"Asked to transmit frame type %s, while native formats is %s (read/write = (%s/%s)\n",
+					"Asked to transmit frame type %s, while native formats is %s (read/write = (%s/%s)) (raw read/write = (%s/%s))\n",
 					ast_getformatname(frame->subclass.codec),
 					ast_getformatname_multiple(tmp, sizeof(tmp), ast->nativeformats),
 					ast_getformatname(ast->readformat),
-					ast_getformatname(ast->writeformat));
+					ast_getformatname(ast->writeformat),
+                                        ast_getformatname(ast->rawwriteformat),
+                                        ast_getformatname(ast->rawreadformat));
 			return -1;
 		}
 	}
@@ -4123,7 +4825,11 @@
 		return "Key Radio";
 	case AST_CONTROL_RADIO_UNKEY:
 		return "Un-Key Radio";
-	case -1:
+        case AST_CONTROL_CONNECTED_LINE:
+                return "Remote end changed";
+        case AST_CONTROL_SRCCHANGE:
+                return "RTP source updated";
+        case -1:
 		return "Stop tone";
 	}
 	return "UNKNOWN";
@@ -4164,7 +4870,7 @@
 	switch (ind) {
 	case AST_CONTROL_RINGING:
 		if (ast->_state != AST_STATE_UP) {
-			send_text(TEXT_LINE2, TEXT_NORMAL, s, "Ringing...");
+			send_text(TEXT_LINE2, TEXT_NORMAL, s, ustmtext("Ringing...", s));
 			in_band_indication(ast, l->parent->tz, "ring");
 			s->device->missed_call = -1;
 			break;
@@ -4173,7 +4879,7 @@
 	case AST_CONTROL_BUSY:
 		if (ast->_state != AST_STATE_UP) {
 			sub->alreadygone = 1;
-			send_text(TEXT_LINE2, TEXT_NORMAL, s, "Busy");
+			send_text(TEXT_LINE2, TEXT_NORMAL, s, ustmtext("Busy", s));
 			in_band_indication(ast, l->parent->tz, "busy");
 			s->device->missed_call = -1;
 			break;
@@ -4182,7 +4888,7 @@
 	case AST_CONTROL_CONGESTION:
 		if (ast->_state != AST_STATE_UP) {
 			sub->alreadygone = 1;
-			send_text(TEXT_LINE2, TEXT_NORMAL, s, "Congestion");
+			send_text(TEXT_LINE2, TEXT_NORMAL, s, ustmtext("Congestion", s));
 			in_band_indication(ast, l->parent->tz, "congestion");
 			s->device->missed_call = -1;
 			break;
@@ -4203,6 +4909,14 @@
 		break;
 	case AST_CONTROL_PROCEEDING:
 		break;
+        case AST_CONTROL_CONNECTED_LINE:
+                ast_log(LOG_NOTICE, "Connected party is now %s <%s>\n",
+		S_COR(ast->connected.id.name.valid, ast->connected.id.name.str, ""),
+		S_COR(ast->connected.id.number.valid, ast->connected.id.number.str, "")); /* TODO */
+                break;
+        case AST_CONTROL_SRCCHANGE:
+                ast_rtp_instance_change_source(sub->rtp);
+                break;
 	default:
 		ast_log(LOG_WARNING, "Don't know how to indicate condition %d\n", ind);
 		return -1;
@@ -4215,6 +4929,7 @@
 {
 	struct unistim_line *l;
 	struct unistim_device *d;
+        struct unistim_subchannel *sub = NULL;
 	char line[256];
 	char *at;
 	char *device;
@@ -4238,12 +4953,22 @@
 			if (unistimdebug)
 				ast_verb(0, "Found device: %s\n", d->name);
 			/* Found the device */
-			l = d->lines;
-			while (l) {
+                        AST_LIST_LOCK(&d->lines);
+                        AST_LIST_TRAVERSE(&d->lines, l, list) {
 				/* Search for the right line */
 				if (!strcasecmp(l->name, line)) {
-					l->subs[SUB_REAL]->ringvolume = -1;
-					l->subs[SUB_REAL]->ringstyle = -1;
+                                        if (unistimdebug)
+                                                ast_verb(0, "Found line: %s\n", l->name);
+                                        sub = get_sub(d, SUB_REAL);
+                                        if (!sub) {
+                                                sub = alloc_sub(d, SUB_REAL);
+                                        }
+                                        if (sub->owner) {
+                                                /* Allocate additional channel if asterisk channel already here */
+                                                sub = alloc_sub(d, SUB_ONHOLD);
+                                        }
+					sub->ringvolume = -1;
+					sub->ringstyle = -1;
 					if (at) {       /* Other options ? */
 						at++;   /* Skip slash */
 						if (*at == 'r') {       /* distinctive ring */
@@ -4259,16 +4984,20 @@
 								if (unistimdebug)
 									ast_verb(0, "Distinctive ring : style #%d volume %d\n",
 										 ring_style, ring_volume);
-								l->subs[SUB_REAL]->ringvolume = ring_volume;
-								l->subs[SUB_REAL]->ringstyle = ring_style;
+								sub->ringvolume = ring_volume;
+								sub->ringstyle = ring_style;
 							}
 						}
 					}
-					ast_mutex_unlock(&devicelock);
-					return l->subs[SUB_REAL];
+                                        sub->parent = l;
+                                        break;
 				}
-				l = l->next;
 			}
+                        AST_LIST_UNLOCK(&d->lines);
+                        if (sub) {
+                            ast_mutex_unlock(&devicelock);
+                            return sub;
+                        }
 		}
 		d = d->next;
 	}
@@ -4294,7 +5023,7 @@
 	struct ast_frame f = { 0, };
 	struct unistim_subchannel *sub;
 
-	sub = pte->device->lines->subs[SUB_REAL];
+	sub = get_sub(pte->device, SUB_REAL);
 
 	if (!sub->owner || sub->alreadygone) {
 		ast_log(LOG_WARNING, "Unable to find subchannel in dtmf senddigit_end\n");
@@ -4429,7 +5158,7 @@
 		if (pte->device->height == 1) {
 			send_text(TEXT_LINE0, TEXT_NORMAL, pte, text);
 		} else {
-			send_text(TEXT_LINE0, TEXT_NORMAL, pte, "Message :");
+			send_text(TEXT_LINE0, TEXT_NORMAL, pte, ustmtext("Message :", pte));
 			send_text(TEXT_LINE1, TEXT_NORMAL, pte, text);
 		}
 		if (size <= TEXT_LENGTH_MAX) {
@@ -4457,7 +5186,7 @@
 	struct ast_event *event;
 	int new;
 	char *mailbox, *context;
-	struct unistim_line *peer = s->device->lines;
+	struct unistim_line *peer = get_main_line(s->device);
 
 	context = mailbox = ast_strdupa(peer->mailbox);
 	strsep(&context, "@");
@@ -4511,9 +5240,9 @@
 	}
 	l = sub->parent;
 	tmp = ast_channel_alloc(1, state, l->cid_num, NULL, l->accountcode, l->exten,
-		l->context, linkedid, l->amaflags, "%s@%s-%d", l->name, l->parent->name, sub->subtype);
+		l->context, linkedid, l->amaflags, "USTM/%s@%s-%d", l->name, l->parent->name, sub->subtype);
 	if (unistimdebug)
-		ast_verb(0, "unistim_new sub=%d (%p) chan=%p\n", sub->subtype, sub, tmp);
+		ast_verb(0, "unistim_new sub=%d (%p) chan=%p line=%s\n", sub->subtype, sub, tmp, l->name);
 	if (!tmp) {
 		ast_log(LOG_WARNING, "Unable to allocate channel structure\n");
 		return NULL;
@@ -4551,8 +5280,8 @@
 	tmp->rawreadformat = fmt;
 	tmp->tech_pvt = sub;
 	tmp->tech = &unistim_tech;
-	if (!ast_strlen_zero(l->language))
-		ast_string_field_set(tmp, language, l->language);
+	if (!ast_strlen_zero(l->parent->language))
+		ast_string_field_set(tmp, language, l->parent->language);
 	sub->owner = tmp;
 	ast_mutex_lock(&usecnt_lock);
 	usecnt++;
@@ -4576,14 +5305,14 @@
 		}
 	}
 	tmp->priority = 1;
-	if (state != AST_STATE_DOWN) {
-		if (unistimdebug)
-			ast_verb(0, "Starting pbx in unistim_new\n");
-		if (ast_pbx_start(tmp)) {
-			ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
-			ast_hangup(tmp);
-			tmp = NULL;
-		}
+        if (state != AST_STATE_DOWN) {
+                if (unistimdebug)
+                        ast_verb(0, "Starting pbx in unistim_new\n");
+                if (ast_pbx_start(tmp)) {
+                        ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
+                        ast_hangup(tmp);
+                        tmp = NULL;
+                }
 	}
 
 	return tmp;
@@ -4632,8 +5361,8 @@
 				dw_timeout = cur->timeout - tick;
 			/* Checking if the phone is logged on for a new MWI */
 			if (cur->device) {
-				if ((!ast_strlen_zero(cur->device->lines->mailbox)) &&
-					((tick >= cur->device->lines->nextmsgcheck))) {
+				if ((!ast_strlen_zero(get_main_line(cur->device)->mailbox)) &&
+					((tick >= get_main_line(cur->device)->nextmsgcheck))) {
 					DEBUG_TIMER("Checking mailbox for MWI\n");
 					unistim_send_mwi_to_peer(cur, tick);
 					break;
@@ -4704,9 +5433,11 @@
 {
 	format_t oldformat;
 	struct unistim_subchannel *sub;
+        struct unistim_device *d;
 	struct ast_channel *tmpc = NULL;
 	char tmp[256];
 	char *dest = data;
+        int i, fl = 0;
 
 	oldformat = format;
 	format &= CAPABILITY;
@@ -4734,12 +5465,30 @@
 		*cause = AST_CAUSE_CONGESTION;
 		return NULL;
 	}
+        sub->subtype = SUB_RING;
+        d = sub->parent->parent;
+        for (i = 0; i < FAVNUM; i++) {
+                if (!soft_key_visible(d, i))
+                        continue;
+                if (d->ssub[i])
+                        continue;
+                if (is_key_line(d, i) && !strcmp(sub->parent->name, d->sline[i]->name)) {
+                        if (unistimdebug)
+                                ast_verb(0, "Found softkey %d for line %s\n", i, sub->parent->name);
+                        d->ssub[i] = sub;
+                        sub->softkey = i;
+                        d->selected = i;
+                        fl = 1;
+                        break;
+                }
+        }
 
 	ast_verb(3, "unistim_request(%s)\n", tmp);
-	/* Busy ? */
-	if (sub->owner) {
+	/* Found no keys for placing call - busy */
+	if (!fl) {
 		if (unistimdebug)
-			ast_verb(0, "Can't create channel : Busy !\n");
+			ast_verb(0, "Can't create channel: Busy!\n");
+                unalloc_sub(d, sub);
 		*cause = AST_CAUSE_BUSY;
 		return NULL;
 	}
@@ -4761,7 +5510,6 @@
 	struct unistim_line *line;
 	struct unistim_subchannel *sub;
 	struct unistimsession *s;
-	int i;
 	struct ast_channel *tmp;
 
 	switch (cmd) {
@@ -4779,36 +5527,47 @@
 	if (a->argc != e->args)
 		return CLI_SHOWUSAGE;
 
-	ast_cli(a->fd, "Dumping internal structures :\ndevice\n->line\n-->sub\n");
+	ast_cli(a->fd, "Dumping internal structures :\ndevice\n->line\n-->sub\n==>line\n");
 	while (device) {
-		ast_cli(a->fd, "\nname=%s id=%s line=%p ha=%p sess=%p device=%p\n",
-				device->name, device->id, device->lines, device->ha, device->session,
-				device);
-		line = device->lines;
-		while (line) {
+                int i;
+
+		ast_cli(a->fd, "\nname=%s id=%s line=%p ha=%p sess=%p device=%p selected=%d height=%d\n",
+				device->name, device->id, get_main_line(device), device->ha, device->session,
+				device, device->selected, device->height);
+		AST_LIST_LOCK(&device->lines);
+                AST_LIST_TRAVERSE(&device->lines,line,list) {
 			ast_cli(a->fd,
 					"->name=%s fullname=%s exten=%s callid=%s cap=%" PRId64 " device=%p line=%p\n",
 					line->name, line->fullname, line->exten, line->cid_num,
 					line->capability, line->parent, line);
-			for (i = 0; i < MAX_SUBS; i++) {
-				sub = line->subs[i];
-				if (!sub)
-					continue;
-				if (!sub->owner)
-					tmp = (void *) -42;
-				else
-					tmp = sub->owner->_bridge;
-				if (sub->subtype != i)
-					ast_cli(a->fd, "Warning ! subchannel->subs[%d] have a subtype=%d\n", i,
-							sub->subtype);
-				ast_cli(a->fd,
-						"-->subtype=%d chan=%p rtp=%p bridge=%p line=%p alreadygone=%d\n",
-						sub->subtype, sub->owner, sub->rtp, tmp, sub->parent,
-						sub->alreadygone);
-			}
-			line = line->next;
 		}
-		device = device->next;
+		AST_LIST_UNLOCK(&device->lines);
+                
+                AST_LIST_LOCK(&device->subs);
+                AST_LIST_TRAVERSE(&device->subs, sub, list) {
+                        if (!sub)
+                                continue;
+                        if (!sub->owner)
+                                tmp = (void *) -42;
+                        else
+                                tmp = sub->owner->_bridge;
+                        ast_cli(a->fd,
+                                        "-->subtype=%d chan=%p rtp=%p bridge=%p line=%p alreadygone=%d softkey=%d\n",
+                                        sub->subtype, sub->owner, sub->rtp, tmp, sub->parent,
+                                        sub->alreadygone, sub->softkey);
+                }
+                AST_LIST_UNLOCK(&device->subs);
+
+                for (i = 0; i < FAVNUM; i++) {
+                        if (!soft_key_visible(device, i)) {
+                            continue;
+                        }
+                        ast_cli(a->fd, "==> %d. dev=%s icon=%d label=%-8s number=%-5s sub=%p line=%p\n",
+				i, device->softkeydevice[i], device->softkeyicon[i], device->softkeylabel[i], device->softkeynumber[i],
+				device->ssub[i], device->sline[i]);
+                }
+
+                device = device->next;
 	}
 	ast_cli(a->fd, "\nSessions:\n");
 	ast_mutex_lock(&sessionlock);
@@ -5080,12 +5839,26 @@
 	}
 }
 
+static struct unistim_line *find_line_by_number(struct unistim_device *d, const char *val) {
+    struct unistim_line *l, *ret = NULL;
+
+    AST_LIST_LOCK(&d->lines);
+    AST_LIST_TRAVERSE(&d->lines, l, list) {
+        if (!strcmp(l->name, val)) {
+            ret = l;
+            break;
+        }
+    }
+    AST_LIST_UNLOCK(&d->lines);
+    return ret;
+}
+
 static struct unistim_device *build_device(const char *cat, const struct ast_variable *v)
 {
 	struct unistim_device *d;
-	struct unistim_line *l = NULL;
+	struct unistim_line *l = NULL, *lt = NULL;
 	int create = 1;
-	int nbsoftkey, dateformat, timeformat, callhistory;
+	int nbsoftkey, dateformat, timeformat, callhistory, sharpdial;
 	char linelabel[AST_MAX_EXTENSION];
 	char context[AST_MAX_EXTENSION];
 	char ringvolume, ringstyle;
@@ -5105,22 +5878,55 @@
 			}
 			/* we're reloading right now */
 			create = 0;
-			l = d->lines;
+                        /* l = d->lines;*/
 			break;
 		}
 		d = d->next;
 	}
+        if (!(lt = ast_calloc(1, sizeof(*lt)))) {
+            return NULL;
+        }
 	ast_mutex_unlock(&devicelock);
 	if (create) {
+/*
+                struct unistim_subchannel *sub = NULL;
+*/
 		if (!(d = ast_calloc(1, sizeof(*d))))
 			return NULL;
 
-		if (!(l = ast_calloc(1, sizeof(*l)))) {
-			ast_free(d);
-			return NULL;
-		}
-		ast_copy_string(d->name, cat, sizeof(d->name));
-	}
+/*
+                sub = ast_calloc(1, sizeof(*sub));
+                if (!sub) {
+                        ast_free(d);
+                        return NULL;
+                }
+                sub->parent = NULL;
+                AST_LIST_LOCK(&d->subs);
+                AST_LIST_INSERT_TAIL(&d->subs, sub, list);
+                AST_LIST_UNLOCK(&d->subs);
+*/
+
+                ast_copy_string(d->name, cat, sizeof(d->name));
+	} else {
+            /* Delete existing line information */
+            AST_LIST_LOCK(&d->lines);
+            AST_LIST_TRAVERSE_SAFE_BEGIN(&d->lines, l, list){
+                AST_LIST_REMOVE_CURRENT(list);
+                ast_free(l);
+            }
+            AST_LIST_TRAVERSE_SAFE_END
+            AST_LIST_UNLOCK(&d->lines);
+        }
+
+        /* reset bookmarks */
+        memset(d->softkeylabel, 0, sizeof(d->softkeylabel));
+        memset(d->softkeynumber, 0, sizeof(d->softkeynumber));
+        memset(d->softkeyicon, 0, sizeof(d->softkeyicon));
+        memset(d->softkeydevice, 0, sizeof(d->softkeydevice));
+        memset(d->ssub, 0, sizeof(d->ssub));
+        memset(d->sline, 0, sizeof(d->sline));
+        memset(d->sp, 0, sizeof(d->sp));
+        
 	ast_copy_string(context, DEFAULTCONTEXT, sizeof(context));
 	d->contrast = -1;
 	d->output = OUTPUT_HANDSET;
@@ -5128,11 +5934,13 @@
 	d->volume = VOLUME_LOW;
 	d->mute = MUTE_OFF;
 	d->height = DEFAULTHEIGHT;
+        d->selected = -1;
 	linelabel[0] = '\0';
 	dateformat = 1;
 	timeformat = 1;
 	ringvolume = 2;
 	callhistory = 1;
+        sharpdial = 0;
 	ringstyle = 3;
 	nbsoftkey = 0;
 	while (v) {
@@ -5176,17 +5984,20 @@
 			ringstyle = atoi(v->value);
 		else if (!strcasecmp(v->name, "callhistory"))
 			callhistory = atoi(v->value);
-		else if (!strcasecmp(v->name, "callerid")) {
+                else if (!strcasecmp(v->name, "sharpdial")) {
+                        if (!strcasecmp(v->value, "yes"))
+                        	sharpdial = 1;
+                } else if (!strcasecmp(v->name, "callerid")) {
 			if (!strcasecmp(v->value, "asreceived"))
-				l->cid_num[0] = '\0';
+				lt->cid_num[0] = '\0';
 			else
-				ast_copy_string(l->cid_num, v->value, sizeof(l->cid_num));
+				ast_copy_string(lt->cid_num, v->value, sizeof(lt->cid_num));
 		} else if (!strcasecmp(v->name, "language"))
-			ast_copy_string(l->language, v->value, sizeof(l->language));
+			ast_copy_string(d->language, v->value, sizeof(d->language));
 		else if (!strcasecmp(v->name, "country"))
 			ast_copy_string(d->country, v->value, sizeof(d->country));
 		else if (!strcasecmp(v->name, "accountcode"))
-			ast_copy_string(l->accountcode, v->value, sizeof(l->accountcode));
+			ast_copy_string(lt->accountcode, v->value, sizeof(lt->accountcode));
 		else if (!strcasecmp(v->name, "amaflags")) {
 			int y;
 			y = ast_cdr_amaflags2int(v->value);
@@ -5194,17 +6005,17 @@
 				ast_log(LOG_WARNING, "Invalid AMA flags: %s at line %d\n", v->value,
 						v->lineno);
 			else
-				l->amaflags = y;
+				lt->amaflags = y;
 		} else if (!strcasecmp(v->name, "musiconhold"))
-			ast_copy_string(l->musicclass, v->value, sizeof(l->musicclass));
+			ast_copy_string(lt->musicclass, v->value, sizeof(lt->musicclass));
 		else if (!strcasecmp(v->name, "callgroup"))
-			l->callgroup = ast_get_group(v->value);
+			lt->callgroup = ast_get_group(v->value);
 		else if (!strcasecmp(v->name, "pickupgroup"))
-			l->pickupgroup = ast_get_group(v->value);
+			lt->pickupgroup = ast_get_group(v->value);
 		else if (!strcasecmp(v->name, "mailbox"))
-			ast_copy_string(l->mailbox, v->value, sizeof(l->mailbox));
+			ast_copy_string(lt->mailbox, v->value, sizeof(lt->mailbox));
 		else if (!strcasecmp(v->name, "parkinglot"))
-			ast_copy_string(l->parkinglot, v->value, sizeof(l->parkinglot));
+			ast_copy_string(lt->parkinglot, v->value, sizeof(lt->parkinglot));
 		else if (!strcasecmp(v->name, "linelabel"))
 			unquote(linelabel, v->value, sizeof(linelabel) - 1);
 		else if (!strcasecmp(v->name, "extension")) {
@@ -5225,74 +6036,66 @@
 					nbsoftkey++;
 			}
 		} else if (!strcasecmp(v->name, "line")) {
-			int len = strlen(linelabel);
+                        int len = strlen(linelabel);
+                        int create_line = 0;
+                        
+                        l = find_line_by_number(d, v->value);
+                        if (!l) { /* If line still not exists */
+                                if (!(l = ast_calloc(1, sizeof(*l)))) {
+                                        ast_free(d);
+                                        return NULL;
+                                }
+                                memcpy(l, lt, sizeof(*l));
+                                ast_mutex_init(&l->lock);
+                                create_line = 1;
+                        }
+                        d->to_delete = 0;
 
-			if (nbsoftkey) {
-				ast_log(LOG_WARNING,
-						"You must use bookmark AFTER line=>. Only one line is supported in this version\n");
-				if (create) {
-					ast_free(d);
-					ast_free(l);
-				}
-				return NULL;
-			}
-			if (create) {
-				ast_mutex_init(&l->lock);
-			} else {
-				d->to_delete = 0;
-				/* reset bookmarks */
-				memset(d->softkeylabel, 0, sizeof(d->softkeylabel));
-				memset(d->softkeynumber, 0, sizeof(d->softkeynumber));
-				memset(d->softkeyicon, 0, sizeof(d->softkeyicon));
-				memset(d->softkeydevice, 0, sizeof(d->softkeydevice));
-				memset(d->sp, 0, sizeof(d->sp));
-			}
-			ast_copy_string(l->name, v->value, sizeof(l->name));
-			snprintf(l->fullname, sizeof(l->fullname), "USTM/%s@%s", l->name, d->name);
-			d->softkeyicon[0] = FAV_ICON_ONHOOK_BLACK;
+                        /* Set softkey info for new line*/
+                        d->sline[nbsoftkey] = l;
+			d->softkeyicon[nbsoftkey] = FAV_ICON_ONHOOK_BLACK;
 			if (!len)		       /* label is undefined ? */
-				ast_copy_string(d->softkeylabel[0], v->value, sizeof(d->softkeylabel[0]));
+				ast_copy_string(d->softkeylabel[nbsoftkey], v->value, sizeof(d->softkeylabel[nbsoftkey]));
 			else {
+                                int softkeylinepos = 0;
 				if ((len > 2) && (linelabel[1] == '@')) {
-					d->softkeylinepos = linelabel[0];
-					if ((d->softkeylinepos >= '0') && (d->softkeylinepos <= '5')) {
-						d->softkeylinepos -= '0';
-						d->softkeyicon[0] = 0;
+					softkeylinepos = linelabel[0];
+					if ((softkeylinepos >= '0') && (softkeylinepos <= '5')) {
+						softkeylinepos -= '0';
+						d->softkeyicon[nbsoftkey] = 0;
 					} else {
 						ast_log(LOG_WARNING,
 								"Invalid position for linelabel : must be between 0 and 5\n");
-						d->softkeylinepos = 0;
 					}
-					ast_copy_string(d->softkeylabel[d->softkeylinepos], linelabel + 2,
-									sizeof(d->softkeylabel[d->softkeylinepos]));
-					d->softkeyicon[d->softkeylinepos] = FAV_ICON_ONHOOK_BLACK;
+					ast_copy_string(d->softkeylabel[softkeylinepos], linelabel + 2,
+									sizeof(d->softkeylabel[softkeylinepos]));
+					d->softkeyicon[softkeylinepos] = FAV_ICON_ONHOOK_BLACK;
 				} else
-					ast_copy_string(d->softkeylabel[0], linelabel,
-									sizeof(d->softkeylabel[0]));
+					ast_copy_string(d->softkeylabel[nbsoftkey], linelabel,
+									sizeof(d->softkeylabel[nbsoftkey]));
 			}
 			nbsoftkey++;
-			ast_copy_string(l->context, context, sizeof(l->context));
-			if (!ast_strlen_zero(l->mailbox)) {
-				if (unistimdebug)
-					ast_verb(3, "Setting mailbox '%s' on %s@%s\n", l->mailbox, d->name, l->name);
-			}
+                        
+                        if (create_line) {
+                                ast_copy_string(l->name, v->value, sizeof(l->name));
+                                snprintf(l->fullname, sizeof(l->fullname), "USTM/%s@%s", l->name, d->name);
+                                ast_copy_string(l->context, context, sizeof(l->context));
+                                if (!ast_strlen_zero(l->mailbox)) {
+                                        if (unistimdebug)
+                                                ast_verb(3, "Setting mailbox '%s' on %s@%s\n", l->mailbox, d->name, l->name);
+                                }
 
-			l->capability = CAPABILITY;
-			l->parent = d;
+                                l->capability = CAPABILITY;
+                                l->parent = d;
 
-			if (create) {
-				if (!alloc_sub(l, SUB_REAL)) {
-					ast_mutex_destroy(&l->lock);
-					ast_free(l);
-					ast_free(d);
-					return NULL;
-				}
-				l->next = d->lines;
-				d->lines = l;
-			}
-		} else if (!strcasecmp(v->name, "height")) {
+                                AST_LIST_LOCK(&d->lines);
+                                AST_LIST_INSERT_TAIL(&d->lines, l, list);
+                                AST_LIST_UNLOCK(&d->lines);
+                        }
+
+                } else if (!strcasecmp(v->name, "height")) {
 			/* Allow the user to lower the expected display lines on the phone
-			 * For example the Nortal I2001 and I2002 only have one ! */
+			 * For example the Nortel I2001 and I2002 only have one ! */
 			d->height = atoi(v->value);
 		} else
 			ast_log(LOG_WARNING, "Don't know keyword '%s' at line %d\n", v->name,
@@ -5302,13 +6105,14 @@
 	d->ringvolume = ringvolume;
 	d->ringstyle = ringstyle;
 	d->callhistory = callhistory;
+        d->sharp_dial = sharpdial;
 	d->tz = ast_get_indication_zone(d->country);
 	if ((d->tz == NULL) && !ast_strlen_zero(d->country))
 		ast_log(LOG_WARNING, "Country '%s' was not found in indications.conf\n",
 				d->country);
 	d->datetimeformat = 56 + (dateformat * 4);
 	d->datetimeformat += timeformat;
-	if (!d->lines) {
+	if (!get_main_line(d)) {
 		ast_log(LOG_ERROR, "An Unistim device must have at least one line!\n");
 		ast_mutex_destroy(&l->lock);
 		ast_free(l);
@@ -5431,8 +6235,23 @@
                 } else if (!strcasecmp(v->name, "cos_audio")) {
                         if (ast_str2cos(v->value, &qos.cos_audio))
                             ast_log(LOG_WARNING, "Invalid cos_audio value at line %d, refer to QoS documentation\n", v->lineno);
-		} else if (!strcasecmp(v->name, "autoprovisioning")) {
+		} else if (!strcasecmp(v->name, "debug")) {
 			if (!strcasecmp(v->value, "no"))
+                                unistimdebug = 0;
+			else if (!strcasecmp(v->value, "yes"))
+				unistimdebug = 1;
+        	} else if (!strcasecmp(v->name, "key_sndhist")) {
+			if (!strcasecmp(v->value, "default"))
+                                key_sndhist = 0;
+			else 
+				key_sndhist = atoi(v->value);
+        	} else if (!strcasecmp(v->name, "key_rcvhist")) {
+			if (!strcasecmp(v->value, "default"))
+                                key_rcvhist = 0;
+			else 
+				key_rcvhist = atoi(v->value);
+                } else if (!strcasecmp(v->name, "autoprovisioning")) {
+			if (!strcasecmp(v->value, "no"))
 				autoprovisioning = AUTOPROVISIONING_NO;
 			else if (!strcasecmp(v->value, "yes"))
 				autoprovisioning = AUTOPROVISIONING_YES;
@@ -5490,44 +6309,57 @@
 	d = devices;
 	while (d) {
 		if (d->to_delete) {
-			int i;
+                        struct unistim_line *l;
+                        struct unistim_subchannel *sub;
 
 			if (unistimdebug)
 				ast_verb(0, "Removing device '%s'\n", d->name);
-			if (!d->lines) {
+			if (!get_main_line(d)) {
 				ast_log(LOG_ERROR, "Device '%s' without a line !, aborting\n", d->name);
 				ast_config_destroy(cfg);
 				return 0;
 			}
-			if (!d->lines->subs[0]) {
-				ast_log(LOG_ERROR, "Device '%s' without a subchannel !, aborting\n",
-						d->name);
-				ast_config_destroy(cfg);
-				return 0;
-			}
-			if (d->lines->subs[0]->owner) {
-				ast_log(LOG_WARNING,
-						"Device '%s' was not deleted : a call is in progress. Try again later.\n",
-						d->name);
-				d = d->next;
-				continue;
-			}
-			ast_mutex_destroy(&d->lines->subs[0]->lock);
-			ast_free(d->lines->subs[0]);
-			for (i = 1; i < MAX_SUBS; i++) {
-				if (d->lines->subs[i]) {
-					ast_log(LOG_WARNING,
-							"Device '%s' with threeway call subchannels allocated, aborting.\n",
-							d->name);
-					break;
-				}
-			}
-			if (i < MAX_SUBS) {
-				d = d->next;
-				continue;
-			}
-			ast_mutex_destroy(&d->lines->lock);
-			ast_free(d->lines);
+
+                        AST_LIST_LOCK(&d->subs);
+                        AST_LIST_TRAVERSE_SAFE_BEGIN(&d->subs, sub, list){
+                            if (sub->subtype == SUB_REAL) {
+                                    if (!sub) {
+                                            ast_log(LOG_ERROR, "Device '%s' without a subchannel !, aborting\n",
+                                                            d->name);
+                                            ast_config_destroy(cfg);
+                                            return 0;
+                                    }
+                                    if (sub->owner) {
+                                            ast_log(LOG_WARNING,
+                                                            "Device '%s' was not deleted : a call is in progress. Try again later.\n",
+                                                            d->name);
+                                            d = d->next;
+                                            continue;
+                                    }
+                            }
+                            if (sub->subtype == SUB_THREEWAY) {
+                                    ast_log(LOG_WARNING,
+                                                    "Device '%s' with threeway call subchannels allocated, aborting.\n",
+                                                    d->name);
+                                    break;
+                            }
+                            AST_LIST_REMOVE_CURRENT(list);
+                            ast_mutex_destroy(&sub->lock);
+                            ast_free(sub);
+                        }
+                        AST_LIST_TRAVERSE_SAFE_END
+                        AST_LIST_UNLOCK(&d->subs);
+
+
+                        AST_LIST_LOCK(&d->lines);
+                        AST_LIST_TRAVERSE_SAFE_BEGIN(&d->lines, l, list){
+                            AST_LIST_REMOVE_CURRENT(list);
+                            ast_mutex_destroy(&l->lock);
+                            ast_free(l);
+                        }
+                        AST_LIST_TRAVERSE_SAFE_END
+                        AST_LIST_UNLOCK(&d->lines);
+
 			if (d->session) {
 				if (sessions == d->session)
 					sessions = d->session->next;
