[svn-commits] igorg: trunk r358766 - in /trunk: ./ channels/ configs/ contrib/unistimLang/

SVN commits to the Digium repositories svn-commits at lists.digium.com
Mon Mar 12 12:01:32 CDT 2012


Author: igorg
Date: Mon Mar 12 12:01:26 2012
New Revision: 358766

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=358766
Log:

Massive changes in chan_unistim channel driver. Include many fixes in channel driver operation and add additional functionality:
 * Added ability to use multiple lines on phone, so for one device in configuration multiple lines can be defined, it allows to have multiple calls on one phone, callwaiting and switching between calls.
 * Added ability for translation on-screen menu to multiple languages. Tested on Russian languages.  Supported encodings: ISO 8859-1, ISO 8859-2, ISO 8859-4, ISO 8859-5, ISO 2022-JP. Language controlled by 'language' and on-screen menu of phone
 * Other described in CHANGES file

Testing done by issue tracker users: ibercom, scsiborg, idarwin, TeknoJuce, c0rnoTa. 
Tested on production system by Jonn Taylor (jonnt) using phone models: Nortel i2004, 1120E and 1140E.

(closes issue ASTERISK-16890)

Review: https://reviewboard.asterisk.org/r/1243/


Added:
    trunk/contrib/unistimLang/
    trunk/contrib/unistimLang/en.po   (with props)
    trunk/contrib/unistimLang/ru.po   (with props)
    trunk/contrib/unistimLang/ru.po.utf8   (with props)
Modified:
    trunk/CHANGES
    trunk/UPGRADE.txt
    trunk/channels/chan_unistim.c
    trunk/configs/unistim.conf.sample

Modified: trunk/CHANGES
URL: http://svnview.digium.com/svn/asterisk/trunk/CHANGES?view=diff&rev=358766&r1=358765&r2=358766
==============================================================================
--- trunk/CHANGES (original)
+++ trunk/CHANGES Mon Mar 12 12:01:26 2012
@@ -79,6 +79,23 @@
 ------------------
  * Added dialtone_detect option for analog ports to disconnect incoming
    calls when dialtone is detected.
+
+Chan_unistim changes
+--------------------
+ * Added ability to use multiple lines on phone, so for one device in 
+   configuration multiple lines can be defined, it allows to have multiple calls
+   on one phone, callwaiting and switching between calls.
+ * Added option 'sharpdial' allowing end dialing by pressing # key
+ * Added options 'cwstyle', 'cwvolume' controlling callwaiting appearance
+ * Added global 'debug' option, that enables debug in channel driver
+ * Added ability for translation on-screen menu to multiple languages. Tested on
+   Russian languages.  Supported encodings: ISO 8859-1, ISO 8859-2, ISO 8859-4, 
+   ISO 8859-5, ISO 2022-JP. Language controlled by 'language' and on-screen 
+   menu of phone
+ * Reworked dialing number input: added dialing by timeout, immediate dial on 
+   on dialplan compare, phone number length now not limited by screen size
+ * Added ability for pickup a call using fetures.conf defined value and 
+   on-screen key
 
 Codec changes
 -------------

Modified: trunk/UPGRADE.txt
URL: http://svnview.digium.com/svn/asterisk/trunk/UPGRADE.txt?view=diff&rev=358766&r1=358765&r2=358766
==============================================================================
--- trunk/UPGRADE.txt (original)
+++ trunk/UPGRADE.txt Mon Mar 12 12:01:26 2012
@@ -83,6 +83,10 @@
    a call is terminated due to RTP stream inactivity or SIP session timer
    expiration.
 
+chan_unistim
+ - Due to massive update in chan_unistim phone keys functions and on-screen 
+   information changed.
+
 users.conf:
  - A defined user with hasvoicemail=yes now finally uses a Gosub to stdexten
    as documented in extensions.conf.sample since v1.6.0 instead of a Macro as

Modified: trunk/channels/chan_unistim.c
URL: http://svnview.digium.com/svn/asterisk/trunk/channels/chan_unistim.c?view=diff&rev=358766&r1=358765&r2=358766
==============================================================================
--- trunk/channels/chan_unistim.c (original)
+++ trunk/channels/chan_unistim.c Mon Mar 12 12:01:26 2012
@@ -73,19 +73,24 @@
 #include "asterisk/musiconhold.h"
 #include "asterisk/causes.h"
 #include "asterisk/indications.h"
-
-/*! Beware, G729 and G723 are not supported by asterisk, except with the proper licence */
+#include "asterisk/features.h"
+#include "asterisk/astobj2.h"
+#include "asterisk/astdb.h"
+
 
 #define DEFAULTCONTEXT	  "default"
 #define DEFAULTCALLERID	 "Unknown"
 #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 */
@@ -93,7 +98,9 @@
 /*! Wait x milliseconds before resending a packet */
 #define RETRANSMIT_TIMER	2000
 /*! How often the mailbox is checked for new messages */
-#define TIMER_MWI	       10000
+#define TIMER_MWI	       5000
+/*! How often the mailbox is checked for new messages */
+#define TIMER_DIAL	       4000
 /*! Not used */
 #define DEFAULT_CODEC	   0x00
 #define SIZE_PAGE	       4096
@@ -102,15 +109,15 @@
 #define MAX_ENTRY_LOG	   30
 
 #define SUB_REAL		0
-#define SUB_THREEWAY	    1
-#define MAX_SUBS		2
+#define SUB_RING                1
+#define SUB_THREEWAY            2
+#define SUB_ONHOLD              3
 
 struct ast_format_cap *global_cap;
 
 enum autoprovision {
 	AUTOPROVISIONING_NO = 0,
 	AUTOPROVISIONING_YES,
-	AUTOPROVISIONING_DB,
 	AUTOPROVISIONING_TN
 };
 
@@ -186,6 +193,9 @@
 
 #define FAV_MAX_LENGTH		  0x0A
 
+#define FAVNUM                    6
+#define FAV_LINE_ICON         FAV_ICON_ONHOOK_BLACK
+
 static void dummy(char *unused, ...)
 {
 	return;
@@ -225,17 +235,9 @@
 static struct io_context *io;
 static struct ast_sched_context *sched;
 static struct sockaddr_in public_ip = { 0, };
-/*! give the IP address for the last packet received */
-static struct sockaddr_in address_from;
-/*! size of the sockaddr_in (in WSARecvFrom) */
-static unsigned int size_addr_from = sizeof(address_from);
-/*! Receive buffer address */
-static unsigned char *buff;
+static unsigned char *buff; /*! Receive buffer address */
 static int unistim_reloading = 0;
 AST_MUTEX_DEFINE_STATIC(unistim_reload_lock);
-AST_MUTEX_DEFINE_STATIC(usecnt_lock);
-static int usecnt = 0;
-/* extern char ast_config_AST_LOG_DIR[AST_CONFIG_MAX_PATH]; */
 
 /*! This is the thread for the monitor which checks for input on the channels
  * which are not currently in use.  */
@@ -257,7 +259,9 @@
 	STATE_DIALPAGE,
 	STATE_RINGING,
 	STATE_CALL,
+	STATE_SELECTOPTION,
 	STATE_SELECTCODEC,
+	STATE_SELECTLANGUAGE,
 	STATE_CLEANING,
 	STATE_HISTORY
 };
@@ -308,75 +312,36 @@
 	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}
-};
+static const int dtmf_row[] = { 697,  770,  852,  941 };
+static const float dtmf_col[] = { 1209, 1336, 1477, 1633 };
 
 struct wsabuf {
 	u_long len;
 	unsigned char *buf;
 };
 
-struct systemtime {
-	unsigned short w_year;
-	unsigned short w_month;
-	unsigned short w_day_of_week;
-	unsigned short w_day;
-	unsigned short w_hour;
-	unsigned short w_minute;
-	unsigned short w_second;
-	unsigned short w_milliseconds;
-};
-
 struct unistim_subchannel {
 	ast_mutex_t lock;
-	/*! SUBS_REAL or SUBS_THREEWAY */
-	unsigned int subtype;
-	/*! Asterisk channel used by the subchannel */
-	struct ast_channel *owner;
-	/*! Unistim line */
-	struct unistim_line *parent;
-	/*! RTP handle */
-	struct ast_rtp_instance *rtp;
+	unsigned int subtype;		/*! SUB_REAL, SUB_RING, SUB_THREEWAY or SUB_ONHOLD */
+	struct ast_channel *owner;	/*! Asterisk channel used by the subchannel */
+	struct unistim_line *parent;	/*! Unistim line */
+	struct ast_rtp_instance *rtp;	/*! RTP handle */
+	int softkey;			/*! Softkey assigned */
+	pthread_t ss_thread;		/*! unistim_ss thread handle */
 	int alreadygone;
 	char ringvolume;
 	char ringstyle;
+	int moh;					/*!< Music on hold in progress */
+	AST_LIST_ENTRY(unistim_subchannel) list;
 };
 
 /*!
@@ -384,62 +349,45 @@
  */
 struct unistim_line {
 	ast_mutex_t lock;
-	/*! Like 200 */
-	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 */
-	char mailbox[AST_MAX_EXTENSION];
-	/*! Used by MWI */
-	int lastmsgssent;
-	/*! Used by MWI */
-	time_t nextmsgcheck;
-	/*! MusicOnHold class */
-	char musicclass[MAX_MUSICCLASS];
-	/*! Call group */
-	unsigned int callgroup;
-	/*! Pickup group */
-	unsigned int pickupgroup;
-	/*! Account code (for billing) */
-	char accountcode[80];
-	/*! AMA flags (for billing) */
-	int amaflags;
-	/*! Codec supported */
-	struct ast_format_cap *cap;
-	/*! Parkinglot */
-	char parkinglot[AST_MAX_CONTEXT];
+	char name[80]; /*! Like 200 */
+	char fullname[80]; /*! Like USTM/200\@black */
+	char exten[AST_MAX_EXTENSION]; /*! Extension where to start */
+	char cid_num[AST_MAX_EXTENSION]; /*! CallerID Number */
+	char mailbox[AST_MAX_EXTENSION]; /*! Mailbox for MWI */
+	int lastmsgssent; /*! Used by MWI */
+	time_t nextmsgcheck; /*! Used by MWI */
+	char musicclass[MAX_MUSICCLASS]; /*! MusicOnHold class */
+	ast_group_t callgroup; /*! Call group */
+	ast_group_t pickupgroup; /*! Pickup group */
+	char accountcode[AST_MAX_ACCOUNT_CODE]; /*! Account code (for billing) */
+	int amaflags; /*! AMA flags (for billing) */
+	struct ast_format_cap *cap; /*! Codec supported */
+	char parkinglot[AST_MAX_CONTEXT]; /*! Parkinglot */
 	struct unistim_line *next;
 	struct unistim_device *parent;
+	AST_LIST_ENTRY(unistim_line) list;
 };
 
-/*! 
- * \brief A device containing one or more lines 
+/*!
+ * \brief A device containing one or more lines
  */
 static struct unistim_device {
+	ast_mutex_t lock;
 	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 */
-	int phone_current;		      /*!< Number of the current phone */
-	int pos_fav;			    /*!< Position of the displayed favorites (used for scrolling) */
+	char context[AST_MAX_EXTENSION]; /*!< Context to start in */
+	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 */
 	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 */
+	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 */
@@ -451,27 +399,31 @@
 	struct ast_tone_zone *tz;	       /*!< Tone zone for res_indications (ring, busy, congestion) */
 	char ringvolume;			/*!< Ring volume */
 	char ringstyle;			 /*!< Ring melody */
+	char cwvolume;			/*!< Ring volume on call waiting */
+	char cwstyle;			 /*!< Ring melody on call waiting */
+	time_t nextdial;		/*!< Timer used for dial by timeout */
 	int rtp_port;			   /*!< RTP port used by the phone */
 	int rtp_method;			 /*!< Select the unistim data used to establish a RTP session */
 	int status_method;		      /*!< Select the unistim packet used for sending status text */
 	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 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 */
 	int output;				     /*!< Handset, headphone or speaker */
 	int previous_output;	    /*!< Previous output */
 	int volume;				     /*!< Default volume */
+        int selected;                           /*!< softkey selected */
 	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;
+	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;
@@ -498,6 +450,21 @@
 	struct unistimsession *next;
 } *sessions = NULL;
 
+/*! Store on screen phone menu item (label and handler function) */
+struct unistim_menu_item {
+	char *label;
+	int state;
+	void (*handle_option)(struct unistimsession *);
+};
+
+/*! Language item for currently existed translations */
+struct unistim_languages {
+	char *label;
+	char *lang_short;
+	int encoding;
+	struct ao2_container *trans;
+};
+
 /*!
  * \page Unistim datagram formats
  *
@@ -516,6 +483,8 @@
 
 static const unsigned char packet_recv_firm_version[] =
 	{ 0x00, 0x00, 0x00, 0x13, 0x9a, 0x0a, 0x02 };
+static const unsigned char packet_recv_it_type[] =
+	{ 0x00, 0x00, 0x00, 0x13, 0x9a, 0x04, 0x03 };
 static const unsigned char packet_recv_pressed_key[] =
 	{ 0x00, 0x00, 0x00, 0x13, 0x99, 0x04, 0x00 };
 static const unsigned char packet_recv_pick_up[] =
@@ -629,9 +598,9 @@
 };
 static const unsigned char packet_send_Contrast[] =
 	{ 0x17, 0x04, 0x24, /*Contrast */ 0x08 };
-static const unsigned char packet_send_StartTimer[] =
-	{ 0x17, 0x05, 0x0b, 0x05, 0x00, 0x17, 0x08, 0x16, /* Text */ 0x44, 0x75, 0x72, 0xe9,
-0x65 };
+static const unsigned char packet_send_start_timer[] =
+	{ 0x17, 0x05, 0x0b, /*Timer option*/0x05, /* Timer ID */0x00, 0x17, 0x08, 0x16,
+	/* Text */ 0x44, 0x75, 0x72, 0xe9, 0x65 };
 static const unsigned char packet_send_stop_timer[] = { 0x17, 0x05, 0x0b, 0x02, 0x00 };
 static const unsigned char packet_send_icon[] = { 0x17, 0x05, 0x14, /*pos */ 0x00, /*icon */ 0x25 };      /* display an icon in front of the text zone */
 static const unsigned char packet_send_S7[] = { 0x17, 0x06, 0x0f, 0x30, 0x07, 0x07 };
@@ -663,6 +632,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 };
@@ -685,7 +671,7 @@
 static int unload_module(void);
 static int reload_config(void);
 static void show_main_page(struct unistimsession *pte);
-static struct ast_channel *unistim_request(const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor, 
+static struct ast_channel *unistim_request(const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor,
 	const char *dest, int *cause);
 static int unistim_call(struct ast_channel *ast, const char *dest, int timeout);
 static int unistim_hangup(struct ast_channel *ast);
@@ -722,15 +708,143 @@
 	.bridge = ast_rtp_instance_bridge,
 };
 
+static void send_start_rtp(struct unistim_subchannel *);
+
+static void send_callerid_screen(struct unistimsession *, struct unistim_subchannel *);
+static void key_favorite(struct unistimsession *, char);
+
+static void handle_select_codec(struct unistimsession *);
+static void handle_select_language(struct unistimsession *);
+static int find_language(const char*);
+
+static int unistim_free_sub(struct unistim_subchannel *);
+
+static struct unistim_menu_item options_menu[] =
+{
+	{"Change codec", STATE_SELECTCODEC, handle_select_codec},
+	{"Language", STATE_SELECTLANGUAGE, handle_select_language},
+	{NULL, 0, NULL}
+};
+
+static struct unistim_languages options_languages[] =
+{
+	{"English", "en", ISO_8859_1, NULL},
+	{"Russian", "ru", ISO_8859_5, NULL},
+	{NULL, NULL, 0, NULL}
+};
+
+static char ustm_strcopy[1024];
+
+struct ustm_lang_entry {
+	const char *str_orig;
+	const char *str_trans;
+};
+
+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 = NULL;
+	int size;
+	
+	if (pte->device) {
+		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;
+
+		if (!(lang->trans = ao2_container_alloc(8, lang_hash_fn, lang_cmp_fn))) {
+			ast_log(LOG_ERROR, "Unable to allocate container for translation!\n");
+			return str;
+		}
+		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) {
+			ast_log(LOG_ERROR, "There is no translation file for '%s'\n", pte->device->language);
+			return str;
+		}
+		while (fgets(tmp, sizeof(tmp), f)) {
+			if (!(p = strchr(tmp, '\n'))) {
+				ast_log(LOG_ERROR, "Too long line found in language file - truncated!\n");
+				continue;
+			}
+			*p = '\0';
+			if (!(p = strchr(tmp, '"'))) {
+				continue;
+			}
+			if (tmp == strstr(tmp, "msgid")) {
+				p_orig = ast_strdup(p + 1);
+				p = strchr(p_orig, '"');
+			} else if (tmp == strstr(tmp, "msgstr")) {
+				p_trans = ast_strdup(p + 1);
+				p = strchr(p_trans, '"');
+			} else {
+				continue;
+			}
+			*p = '\0';
+			if (!p_trans || !p_orig) {
+				continue;
+			}
+			if (ast_strlen_zero(p_trans)) {
+				ast_free(p_trans);
+				ast_free(p_orig);
+				p_trans = NULL;
+				p_orig = NULL;
+				continue;
+			}
+			if (!(lang_entry = ao2_alloc(sizeof(*lang_entry), NULL))) {
+				fclose(f);
+				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 = str;
+	if ((lang_entry = ao2_find(lang->trans, &le_search, OBJ_POINTER))) {
+		size = strlen(lang_entry->str_trans)+1;
+	    	if (size > 1024) {
+			size = 1024;
+		}
+		memcpy(ustm_strcopy, lang_entry->str_trans, size);
+		ao2_ref(lang_entry, -1);
+		return ustm_strcopy;
+	}
+
+	return str;
+}
+
 static void display_last_error(const char *sz_msg)
 {
-	time_t cur_time;
-	
-	time(&cur_time);
-
 	/* Display the error message */
-	ast_log(LOG_WARNING, "%s %s : (%u) %s\n", ctime(&cur_time), sz_msg, errno,
-			strerror(errno));
+	ast_log(LOG_WARNING, "%s : (%u) %s\n", sz_msg, errno, strerror(errno));
 }
 
 static unsigned int get_tick_count(void)
@@ -789,8 +903,9 @@
 	}
 #endif
 
-	if (sendmsg(unistimsock, &msg, 0) == -1)
+	if (sendmsg(unistimsock, &msg, 0) == -1) {
 		display_last_error("Error sending datas");
+	}
 #else
 	if (sendto(unistimsock, data, size, 0, (struct sockaddr *) addr_to, sizeof(*addr_to))
 		== -1)
@@ -820,8 +935,9 @@
 	pte->timeout = tick + RETRANSMIT_TIMER;
 
 /*#ifdef DUMP_PACKET */
-	if (unistimdebug)
+	if (unistimdebug) {
 		ast_verb(6, "Sending datas with seq #0x%.4x Using slot #%d :\n", pte->seq_server, buf_pos);
+	}
 /*#endif */
 	send_raw_client(pte->wsabufsend[buf_pos].len, pte->wsabufsend[buf_pos].buf, &(pte->sin),
 				  &(pte->sout));
@@ -832,8 +948,9 @@
 static void send_ping(struct unistimsession *pte)
 {
 	BUFFSEND;
-	if (unistimdebug)
+	if (unistimdebug) {
 		ast_verb(6, "Sending ping\n");
+	}
 	pte->tick_next_ping = get_tick_count() + unistim_keepalive;
 	memcpy(buffsend + SIZE_HEADER, packet_send_ping, sizeof(packet_send_ping));
 	send_client(SIZE_HEADER + sizeof(packet_send_ping), buffsend, pte);
@@ -860,8 +977,9 @@
 	msg.msg_controllen = sizeof(ip_msg);
 	/* Get info about the incoming packet */
 	err = recvmsg(fd, &msg, MSG_PEEK);
-	if (err == -1)
+	if (err == -1) {
 		ast_log(LOG_WARNING, "recvmsg returned an error: %s\n", strerror(errno));
+	}
 	memcpy(&toAddr->sin_addr, &ip_msg.address, sizeof(struct in_addr));
 	return err;
 #else
@@ -893,11 +1011,6 @@
 	sessions = s;
 
 	s->timeout = get_tick_count() + RETRANSMIT_TIMER;
-	s->seq_phone = (short) 0x0000;
-	s->seq_server = (short) 0x0000;
-	s->last_seq_ack = (short) 0x000;
-	s->last_buf_available = 0;
-	s->nb_retransmit = 0;
 	s->state = STATE_INIT;
 	s->tick_next_ping = get_tick_count() + unistim_keepalive;
 	/* Initialize struct wsabuf  */
@@ -911,8 +1024,9 @@
 static void send_end_call(struct unistimsession *pte)
 {
 	BUFFSEND;
-	if (unistimdebug)
+	if (unistimdebug) {
 		ast_verb(0, "Sending end call\n");
+	}
 	memcpy(buffsend + SIZE_HEADER, packet_send_end_call, sizeof(packet_send_end_call));
 	send_client(SIZE_HEADER + sizeof(packet_send_end_call), buffsend, pte);
 }
@@ -932,48 +1046,54 @@
 {
 	/* Check if our send queue contained only one element */
 	if (pte->last_buf_available == 1) {
-		if (unistimdebug)
+		if (unistimdebug) {
 			ast_verb(6, "Our single packet was ACKed.\n");
+		}
 		pte->last_buf_available--;
 		set_ping_timer(pte);
 		return;
 	}
 	/* Check if this ACK catch up our latest packet */
 	else if (pte->last_seq_ack + 1 == pte->seq_server + 1) {
-		if (unistimdebug)
+		if (unistimdebug) {
 			ast_verb(6, "Our send queue is completely ACKed.\n");
+		}
 		pte->last_buf_available = 0;    /* Purge the send queue */
 		set_ping_timer(pte);
 		return;
 	}
-	if (unistimdebug)
+	if (unistimdebug) {
 		ast_verb(6, "We still have packets in our send queue\n");
+	}
 	return;
 }
 
 static void send_start_timer(struct unistimsession *pte)
 {
 	BUFFSEND;
-	if (unistimdebug)
+	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)
 {
 	BUFFSEND;
-	if (unistimdebug)
+	if (unistimdebug) {
 		ast_verb(0, "Sending stop timer\n");
+	}
 	memcpy(buffsend + SIZE_HEADER, packet_send_stop_timer, sizeof(packet_send_stop_timer));
 	send_client(SIZE_HEADER + sizeof(packet_send_stop_timer), buffsend, pte);
 }
 
-static void Sendicon(unsigned char pos, unsigned char status, struct unistimsession *pte)
+static void send_icon(unsigned char pos, unsigned char status, struct unistimsession *pte)
 {
 	BUFFSEND;
-	if (unistimdebug)
+	if (unistimdebug) {
 		ast_verb(0, "Sending icon pos %d with status 0x%.2x\n", pos, status);
+	}
 	memcpy(buffsend + SIZE_HEADER, packet_send_icon, sizeof(packet_send_icon));
 	buffsend[9] = pos;
 	buffsend[10] = status;
@@ -984,8 +1104,9 @@
 {
 	BUFFSEND;
 	if (!tone1) {
-		if (unistimdebug)
+		if (unistimdebug) {
 			ast_verb(0, "Sending Stream Based Tone Off\n");
+		}
 		memcpy(buffsend + SIZE_HEADER, packet_send_stream_based_tone_off,
 			   sizeof(packet_send_stream_based_tone_off));
 		send_client(SIZE_HEADER + sizeof(packet_send_stream_based_tone_off), buffsend, pte);
@@ -996,8 +1117,9 @@
 	   ast_verb(0, "Sending Stream Based Tone Cadence Download\n");
 	   memcpy (buffsend + SIZE_HEADER, packet_send_StreamBasedToneCad, sizeof (packet_send_StreamBasedToneCad));
 	   send_client (SIZE_HEADER + sizeof (packet_send_StreamBasedToneCad), buffsend, pte); */
-	if (unistimdebug)
+	if (unistimdebug) {
 		ast_verb(0, "Sending Stream Based Tone Frequency Component List Download %d %d\n", tone1, tone2);
+	}
 	tone1 *= 8;
 	if (!tone2) {
 		memcpy(buffsend + SIZE_HEADER, packet_send_stream_based_tone_single_freq,
@@ -1018,8 +1140,9 @@
 				   pte);
 	}
 
-	if (unistimdebug)
+	if (unistimdebug) {
 		ast_verb(0, "Sending Stream Based Tone On\n");
+	}
 	memcpy(buffsend + SIZE_HEADER, packet_send_stream_based_tone_on,
 		   sizeof(packet_send_stream_based_tone_on));
 	send_client(SIZE_HEADER + sizeof(packet_send_stream_based_tone_on), buffsend, pte);
@@ -1027,9 +1150,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,36 +1163,130 @@
 	BUFFSEND;
 	int i;
 
-	if (unistimdebug)
+	if (unistimdebug) {
 		ast_verb(0, "Sending favorite pos %d with status 0x%.2x\n", pos, status);
+	}
 	memcpy(buffsend + SIZE_HEADER, packet_send_favorite, sizeof(packet_send_favorite));
 	buffsend[10] = pos;
 	buffsend[24] = pos;
 	buffsend[25] = status;
-	i = strlen(text);
-	if (i > FAV_MAX_LENGTH)
+	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 int soft_key_visible(struct unistim_device* d, unsigned char num)
+{
+	if(d->height == 1 && num % 3 == 2) {
+		return 0;
+	}
+	return 1;
+}
+
 static void refresh_all_favorite(struct unistimsession *pte)
 {
-	int i = 0;
-
-	if (unistimdebug)
+	unsigned char i = 0;
+	char data[256];
+	struct unistim_line *line;
+	line = AST_LIST_FIRST(&pte->device->lines);
+
+	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]);
-		else
-			send_favorite((unsigned char) i, pte->device->softkeyicon[i], pte,
-						 pte->device->softkeylabel[i]);
-
-	}
-}
+	}
+	for (i = 0; i < FAVNUM; i++) {
+		unsigned char status = pte->device->softkeyicon[i];
+
+		if (!soft_key_visible(pte->device, i)) {
+			continue;
+		}
+		if (!strcasecmp(pte->device->softkeylabel[i], "DND") && line) {
+			if (!ast_db_get("DND", line->name, data, sizeof(data))) {
+				status = FAV_ICON_SPEAKER_ONHOOK_WHITE;
+			}
+		}
+
+		send_favorite_short(i, status, pte);
+	}
+}
+
+static int is_key_favorite(struct unistim_device *d, int fav)
+{
+	if ((fav < 0) && (fav > 5)) {
+		return 0;
+	}
+	if (d->sline[fav]) {
+		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->sline[fav]) {
+		return 0;
+	}
+	if (is_key_favorite(d, fav)) {
+		return 0;
+	}
+	return 1;
+}
+
+static int get_active_softkey(struct unistimsession *pte)
+{
+	return pte->device->selected;
+}
+
+static int get_avail_softkey(struct unistimsession *pte, const char* name)
+{
+	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)) {
+			if (name && strcmp(name, pte->device->sline[i]->name)) {
+				continue;
+			}
+			if (unistimdebug) {
+				ast_verb(0, "Found softkey %d for device %s\n", i, name);
+			}
+			return i;
+		}
+	}
+	return -1;
+}
+
 
 /* Change the status for this phone (pte) and update for each phones where pte is bookmarked
  * use FAV_ICON_*_BLACK constant in status parameters */
@@ -1077,18 +1294,22 @@
 {
 	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;
-					if (d->session)
+					if (d->session) {
 						send_favorite(i, status + 1, d->session, d->softkeylabel[i]);
+					}
 				}
 			}
 		}
@@ -1096,72 +1317,94 @@
 	}
 }
 
-static int RegisterExtension(const struct unistimsession *pte)
-{
-	if (unistimdebug)
+static int register_extension(const struct unistimsession *pte)
+{
+	struct unistim_line *line;
+	line = AST_LIST_FIRST(&pte->device->lines);
+	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, pte->device->context,
+					line->fullname);
+	}
+	return ast_add_extension(pte->device->context, 0,
 							 pte->device->extension_number, 1, NULL, NULL, "Dial",
-							 pte->device->lines->fullname, 0, "Unistim");
-}
-
-static int UnregisterExtension(const struct unistimsession *pte)
-{
-	if (unistimdebug)
+							 line->fullname, 0, "Unistim");
+}
+
+static int unregister_extension(const struct unistimsession *pte)
+{
+	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, pte->device->context);
+	}
+	return ast_context_remove_extension(pte->device->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;
 	/* Looking for the session in the linked chain */
 	while (cur) {
-		if (cur == s)
+		if (cur == s) {
 			break;
+		}
 		prev = cur;
 		cur = cur->next;
 	}
 	if (cur) {				      /* Session found ? */
 		if (cur->device) {	      /* This session was registered ? */
 			s->state = STATE_CLEANING;
-			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]);
+			if (unistimdebug) {
+				ast_verb(0, "close_client session %p device %p\n", s, s->device);
+			}
 			change_favorite_icon(s, FAV_ICON_NONE);
-			sub = s->device->lines->subs[SUB_REAL];
-			if (sub) {
+			ast_mutex_lock(&s->device->lock);
+			AST_LIST_LOCK(&s->device->subs);
+			AST_LIST_TRAVERSE_SAFE_BEGIN(&s->device->subs, sub, list) {
+				if (!sub) {
+					continue;
+				}
 				if (sub->owner) {       /* Call in progress ? */
-					if (unistimdebug)
+					if (unistimdebug) {
 						ast_verb(0, "Aborting call\n");
+					}
 					ast_queue_hangup_with_cause(sub->owner, AST_CAUSE_NETWORK_OUT_OF_ORDER);
+				} else {
+					if (unistimdebug) {
+						ast_debug(1, "Released sub %d of channel %s@%s\n", sub->subtype, sub->parent->name, s->device->name);
+					}
+					AST_LIST_REMOVE_CURRENT(list);
+					unistim_free_sub(sub);
 				}
-			} else
-				ast_log(LOG_WARNING, "Freeing a client with no subchannel !\n");
-			if (!ast_strlen_zero(s->device->extension_number))
-				UnregisterExtension(s);
+			}
+			AST_LIST_TRAVERSE_SAFE_END;
+			AST_LIST_UNLOCK(&s->device->subs);
+
+			if (!ast_strlen_zero(s->device->extension_number)) {
+				unregister_extension(s);
+			}
 			cur->device->session = NULL;
+			ast_mutex_unlock(&s->device->lock);
 		} else {
-			if (unistimdebug)
+			if (unistimdebug) {
 				ast_verb(0, "Freeing an unregistered client\n");
-		}
-		if (prev)
+			}
+		}
+		if (prev) {
 			prev->next = cur->next;
-		else
+		} else {
 			sessions = cur->next;
+		}
 		ast_mutex_destroy(&s->lock);
 		ast_free(s);
-	} else
+	} else {
 		ast_log(LOG_WARNING, "Trying to delete non-existent session %p?\n", s);
+	}
 	ast_mutex_unlock(&sessionlock);
 	return;
 }
@@ -1173,8 +1416,9 @@
 
 	ast_mutex_lock(&pte->lock);
 	if (++pte->nb_retransmit >= NB_MAX_RETRANSMIT) {
-		if (unistimdebug)
+		if (unistimdebug) {
 			ast_verb(0, "Too many retransmit - freeing client\n");
+		}
 		ast_mutex_unlock(&pte->lock);
 		close_client(pte);
 		return 1;
@@ -1212,14 +1456,23 @@
 {
 	int i;
 	BUFFSEND;
-	if (unistimdebug)
+	if (!text) {
+		ast_log(LOG_ERROR, "Asked to display NULL text (pos %d, inverse flag %d)\n", pos, inverse);
+		return;
+	}
+	if (pte->device && pte->device->height == 1 && pos != TEXT_LINE0) {
+		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));
 	buffsend[10] = pos;
 	buffsend[11] = inverse;
 	i = strlen(text);
-	if (i > TEXT_LENGTH_MAX)
+	if (i > TEXT_LENGTH_MAX) {
 		i = TEXT_LENGTH_MAX;
+	}
 	memcpy(buffsend + 12, text, i);
 	send_client(SIZE_HEADER + sizeof(packet_send_text), buffsend, pte);
 }
@@ -1228,8 +1481,9 @@
 {
 	BUFFSEND;
 	int i;
-	if (unistimdebug)
+	if (unistimdebug) {
 		ast_verb(0, "Sending status text\n");
+	}
 	if (pte->device) {
 		if (pte->device->status_method == 1) {  /* For new firmware and i2050 soft phone */
 			int n = strlen(text);
@@ -1251,8 +1505,9 @@
 
 	memcpy(buffsend + SIZE_HEADER, packet_send_status, sizeof(packet_send_status));
 	i = strlen(text);
-	if (i > STATUS_LENGTH_MAX)
+	if (i > STATUS_LENGTH_MAX) {
 		i = STATUS_LENGTH_MAX;
+	}
 	memcpy(buffsend + 10, text, i);
 	send_client(SIZE_HEADER + sizeof(packet_send_status), buffsend, pte);
 
@@ -1265,8 +1520,9 @@
 static void send_led_update(struct unistimsession *pte, unsigned char led)
 {
 	BUFFSEND;
-	if (unistimdebug)
+	if (unistimdebug) {
 		ast_verb(0, "Sending led_update (%x)\n", led);
+	}
 	memcpy(buffsend + SIZE_HEADER, packet_send_led_update, sizeof(packet_send_led_update));
 	buffsend[9] = led;
 	send_client(SIZE_HEADER + sizeof(packet_send_led_update), buffsend, pte);
@@ -1280,67 +1536,78 @@
 				 unsigned char mute)
 {
 	BUFFSEND;
-	if (unistimdebug)
+	if (unistimdebug) {
 		ast_verb(0, "Sending select output packet output=%x volume=%x mute=%x\n", output,
 					volume, mute);
+	}
 	memcpy(buffsend + SIZE_HEADER, packet_send_select_output,
 		   sizeof(packet_send_select_output));
 	buffsend[9] = output;
-	if (output == OUTPUT_SPEAKER)
+	if (output == OUTPUT_SPEAKER) {
 		volume = VOLUME_LOW_SPEAKER;
-	else
+	} else {
 		volume = VOLUME_LOW;
+	}
 	buffsend[10] = volume;
-	if (mute == MUTE_ON_DISCRET)
+	if (mute == MUTE_ON_DISCRET) {
 		buffsend[11] = MUTE_ON;
-	else
+	} else {
 		buffsend[11] = mute;
+	}
 	send_client(SIZE_HEADER + sizeof(packet_send_select_output), buffsend, pte);
-	if (mute == MUTE_OFF)
+	if (mute == MUTE_OFF) {
 		send_led_update(pte, 0x18);
-	else if (mute == MUTE_ON)
+	} else if (mute == MUTE_ON) {
 		send_led_update(pte, 0x19);
+	}
 	pte->device->mute = mute;
 	if (output == OUTPUT_HANDSET) {
-		if (mute == MUTE_ON)
+		if (mute == MUTE_ON) {
 			change_favorite_icon(pte, FAV_ICON_ONHOLD_BLACK);
-		else
+		} else {
 			change_favorite_icon(pte, FAV_ICON_OFFHOOK_BLACK);
+		}
 		send_led_update(pte, 0x08);
 		send_led_update(pte, 0x10);
 	} else if (output == OUTPUT_HEADPHONE) {
-		if (mute == MUTE_ON)
+		if (mute == MUTE_ON) {
 			change_favorite_icon(pte, FAV_ICON_HEADPHONES_ONHOLD);
-		else
+		} else {
 			change_favorite_icon(pte, FAV_ICON_HEADPHONES);
+		}
 		send_led_update(pte, 0x08);
 		send_led_update(pte, 0x11);
 	} else if (output == OUTPUT_SPEAKER) {
 		send_led_update(pte, 0x10);
 		send_led_update(pte, 0x09);
 		if (pte->device->receiver_state == STATE_OFFHOOK) {
-			if (mute == MUTE_ON)
+			if (mute == MUTE_ON) {
 				change_favorite_icon(pte, FAV_ICON_SPEAKER_ONHOLD_BLACK);
-			else
+			} else {
 				change_favorite_icon(pte, FAV_ICON_SPEAKER_ONHOOK_BLACK);
+			}

[... 6352 lines stripped ...]



More information about the svn-commits mailing list