Limit SMS Sending Retries/Handshake Error Patch

hisham malik <[email protected]>
Newsgroups gmane.comp.mobile.kannel.devel
Message-ID <[email protected]>
Attaching more patches for the kannel source.

1. Limit SMS Sending Retries:
Limit SMS sending retries in split/long messages

2. Handshake Error and Garbage Character in Long Message:
Added send_failed call to the message sending function when error is
received during handshake.
Fixed garbage characters in multi-part messages.
handshake_error_n_garbage_in_long_msg.patch (application/octet-stream, 50.6 KB)
Index: smsc_at.c
===================================================================
RCS file: /home/cvs/gateway/gw/smsc/smsc_at.c,v
retrieving revision 1.58
diff -u -r1.58 smsc_at.c
--- smsc_at.c	15 Apr 2009 11:54:11 -0000	1.58
+++ smsc_at.c	30 Apr 2009 09:17:31 -0000
@@ -1,67 +1,67 @@
-/* ==================================================================== 
- * The Kannel Software License, Version 1.0 
- * 
- * Copyright (c) 2001-2009 Kannel Group  
- * Copyright (c) 1998-2001 WapIT Ltd.   
- * All rights reserved. 
- * 
- * Redistribution and use in source and binary forms, with or without 
- * modification, are permitted provided that the following conditions 
- * are met: 
- * 
- * 1. Redistributions of source code must retain the above copyright 
- *    notice, this list of conditions and the following disclaimer. 
- * 
- * 2. Redistributions in binary form must reproduce the above copyright 
- *    notice, this list of conditions and the following disclaimer in 
- *    the documentation and/or other materials provided with the 
- *    distribution. 
- * 
- * 3. The end-user documentation included with the redistribution, 
- *    if any, must include the following acknowledgment: 
- *       "This product includes software developed by the 
- *        Kannel Group (http://www.kannel.org/)." 
- *    Alternately, this acknowledgment may appear in the software itself, 
- *    if and wherever such third-party acknowledgments normally appear. 
- * 
- * 4. The names "Kannel" and "Kannel Group" must not be used to 
- *    endorse or promote products derived from this software without 
- *    prior written permission. For written permission, please  
- *    contact [email protected]. 
- * 
- * 5. Products derived from this software may not be called "Kannel", 
- *    nor may "Kannel" appear in their name, without prior written 
- *    permission of the Kannel Group. 
- * 
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
- * DISCLAIMED.  IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS 
- * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,  
- * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT  
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR  
- * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,  
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE  
- * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,  
- * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
- * ==================================================================== 
- * 
- * This software consists of voluntary contributions made by many 
- * individuals on behalf of the Kannel Group.  For more information on  
- * the Kannel Group, please see <http://www.kannel.org/>. 
- * 
- * Portions of this software are based upon software originally written at  
- * WapIT Ltd., Helsinki, Finland for the Kannel project.  
- */ 
+/* ====================================================================
+ * The Kannel Software License, Version 1.0
+ *
+ * Copyright (c) 2001-2009 Kannel Group
+ * Copyright (c) 1998-2001 WapIT Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ *    if any, must include the following acknowledgment:
+ *       "This product includes software developed by the
+ *        Kannel Group (http://www.kannel.org/)."
+ *    Alternately, this acknowledgment may appear in the software itself,
+ *    if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Kannel" and "Kannel Group" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please
+ *    contact [email protected].
+ *
+ * 5. Products derived from this software may not be called "Kannel",
+ *    nor may "Kannel" appear in their name, without prior written
+ *    permission of the Kannel Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Kannel Group.  For more information on
+ * the Kannel Group, please see <http://www.kannel.org/>.
+ *
+ * Portions of this software are based upon software originally written at
+ * WapIT Ltd., Helsinki, Finland for the Kannel project.
+ */
 
 /*
  * smsc_at.c
- * 
+ *
  * New driver for serial connected AT based
  * devices.
  * 4.9.2001
  * Andreas Fink <[email protected]>
- 
+
  * 23.6.2008, Andreas Fink,
  *   added support for telnet connections
  *   (for example Multi-Tech MTCBA-G-EN-F4)
@@ -108,17 +108,17 @@
     int b;
     int i;
     Octstr *hex;
-   
+
     char answer[5];
 
-    
+
     if(!privdata->ilb)
         return;
     start = 0;
     len = octstr_len(privdata->ilb);
     hex = octstr_duplicate(privdata->ilb);
     octstr_binary_to_hex(hex,1);
-    
+
     octstr_destroy(hex);
 
     while(start < len)
@@ -157,7 +157,7 @@
         }
         start = pos;
     }
-    
+
 }
 
 static int at2_open_device1(PrivAT2data *privdata)
@@ -176,12 +176,12 @@
         if (octstr_str_compare(privdata->device, "rawtcp") == 0) {
             privdata->use_telnet = 0;
             privdata->fd = tcpip_connect_to_server(octstr_get_cstr(privdata->rawtcp_host),
-                                                   privdata->rawtcp_port, NULL); 
+                                                   privdata->rawtcp_port, NULL);
         }
         else if (octstr_str_compare(privdata->device, "telnet") == 0) {
             privdata->use_telnet = 1;
             privdata->fd = tcpip_connect_to_server(octstr_get_cstr(privdata->rawtcp_host),
-                                                   privdata->rawtcp_port, NULL); 
+                                                   privdata->rawtcp_port, NULL);
 
         } else {
             gw_assert(0);
@@ -232,11 +232,11 @@
 
     if (!privdata->is_serial)
         return 0;
-        
+
     tcgetattr(privdata->fd, &tios);
 
     kannel_cfmakeraw(&tios);
-                      
+
     tios.c_iflag |= IGNBRK; /* ignore break & parity errors */
     tios.c_iflag &= ~INPCK; /* INPCK: disable parity check */
     tios.c_cflag |= HUPCL; /* hangup on close */
@@ -263,10 +263,10 @@
               octstr_get_cstr(privdata->name));
     }
     tcflush(privdata->fd, TCIOFLUSH);
-         
-    /* 
+
+    /*
      * Nokia 7110 and 6210 need some time between opening
-     * the connection and sending the first AT commands 
+     * the connection and sending the first AT commands
      */
     if (privdata->modem == NULL || privdata->modem->need_sleep)
         sleep(1);
@@ -298,7 +298,7 @@
     struct timeval tv;
 
     if (privdata->fd == -1) {
-        error(errno, "AT2[%s]: at2_read_buffer: fd = -1. Can not read", 
+        error(errno, "AT2[%s]: at2_read_buffer: fd = -1. Can not read",
               octstr_get_cstr(privdata->name));
         return;
     }
@@ -326,7 +326,7 @@
 
     s = read(privdata->fd, buf, count);
     if (s < 0) {
-        error(errno, "AT2[%s]: at2_read_buffer: Error during read", 
+        error(errno, "AT2[%s]: at2_read_buffer: Error during read",
               octstr_get_cstr(privdata->name));
         at2_close_device(privdata);
     } else {
@@ -376,7 +376,7 @@
 
     if (gt_flag==1) {
         /* looking for > if needed */
-        gtloc = octstr_search_char(privdata->ilb, '>', 0); 
+        gtloc = octstr_search_char(privdata->ilb, '>', 0);
     }
     else if((gt_flag == 2) && (privdata->username)) { /* looking for "Login" */
         gtloc = -1;
@@ -402,7 +402,7 @@
     else
         gtloc = -1;
 
-    /*   
+    /*
     if (gt_flag && (gtloc != -1))
         debug("bb.smsc.at2", 0, "in at2_read_line with gt_flag=1, gtloc=%d, ilb=%s",
               gtloc, octstr_get_cstr(privdata->ilb));
@@ -429,16 +429,16 @@
     octstr_strip_blanks(line);
 
     /* empty line, skipping */
-    if ((strcmp(octstr_get_cstr(line), "") == 0) && ( gt_flag == 0)) 
+    if ((strcmp(octstr_get_cstr(line), "") == 0) && ( gt_flag == 0))
     {
         octstr_destroy(line);
         return NULL;
     }
     if ((gt_flag) && (gtloc != -1)) {
         /* got to re-add it again as the parser needs to see it */
-        octstr_append_cstr(line, ">"); 
+        octstr_append_cstr(line, ">");
     }
-    debug("bb.smsc.at2", 0, "AT2[%s]: <-- %s", octstr_get_cstr(privdata->name), 
+    debug("bb.smsc.at2", 0, "AT2[%s]: <-- %s", octstr_get_cstr(privdata->name),
           octstr_get_cstr(line));
     return line;
 }
@@ -458,7 +458,7 @@
     count = octstr_len(linestr);
     while (count > data_written) {
         errno = 0;
-        s = write(privdata->fd, octstr_get_cstr(linestr) + data_written, 
+        s = write(privdata->fd, octstr_get_cstr(linestr) + data_written,
                   count - data_written);
         if (s < 0 && errno == EAGAIN && write_count < RETRY_WRITE) {
             gwthread_sleep(1);
@@ -471,13 +471,13 @@
     }
     O_DESTROY(linestr);
     if (s < 0) {
-        error(errno, "AT2[%s]: Couldnot write to device.", 
+        error(errno, "AT2[%s]: Couldnot write to device.",
               octstr_get_cstr(privdata->name));
         tcflush(privdata->fd, TCOFLUSH);
         return s;
     }
     tcdrain(privdata->fd);
-    gwthread_sleep((double) (privdata->modem == NULL ? 
+    gwthread_sleep((double) (privdata->modem == NULL ?
         100 : privdata->modem->sendline_sleep) / 1000);
     return s;
 }
@@ -488,7 +488,7 @@
     int s;
     char *ctrlz = "\032" ;
     int write_count = 0;
-    
+
     debug("bb.smsc.at2", 0, "AT2[%s]: --> ^Z", octstr_get_cstr(privdata->name));
     while (1) {
         errno = 0;
@@ -500,7 +500,7 @@
             break;
     }
     if (s < 0) {
-        error(errno, "AT2[%s]: Couldnot write to device.", 
+        error(errno, "AT2[%s]: Couldnot write to device.",
               octstr_get_cstr(privdata->name));
         tcflush(privdata->fd, TCOFLUSH);
         return s;
@@ -510,7 +510,7 @@
         100 : privdata->modem->sendline_sleep) / 1000);
     return s;
 }
-      
+
 
 static int at2_write(PrivAT2data *privdata, char *line)
 {
@@ -609,15 +609,15 @@
 
     /* enable hardware handshake */
     if (octstr_len(privdata->modem->enable_hwhs)) {
-        if (at2_send_modem_command(privdata, 
+        if (at2_send_modem_command(privdata,
             octstr_get_cstr(privdata->modem->enable_hwhs), 0, 0) == -1)
-            info(0, "AT2[%s]: cannot enable hardware handshake", 
+            info(0, "AT2[%s]: cannot enable hardware handshake",
                  octstr_get_cstr(privdata->name));
     }
 
     /*
      * Check does the modem require a PIN and, if so, send it.
-     * This is not supported by the Nokia Premicell 
+     * This is not supported by the Nokia Premicell
      */
     if (!privdata->modem->no_pin) {
         ret = at2_send_modem_command(privdata, "AT+CPIN?", 10, 0);
@@ -635,9 +635,9 @@
                 return -1;
         }
 
-        /* 
+        /*
          * we have to wait until +CPIN: READY appears before issuing
-         * the next command. 10 sec should be suficient 
+         * the next command. 10 sec should be suficient
          */
         if (!privdata->pin_ready) {
             at2_wait_modem_command(privdata, 10, 0, NULL);
@@ -649,8 +649,8 @@
             }
         }
     }
-    /* 
-     * Set the GSM SMS message center address if supplied 
+    /*
+     * Set the GSM SMS message center address if supplied
      */
     if (octstr_len(privdata->sms_center)) {
         Octstr *temp;
@@ -658,7 +658,7 @@
         octstr_append_char(temp, 34);
         octstr_append(temp, privdata->sms_center);
         octstr_append_char(temp, 34);
-        /* 
+        /*
          * XXX If some modem don't process the +, remove it and add ",145"
          * and ",129" to national numbers
          */
@@ -667,7 +667,7 @@
         if (ret == -1)
             return -1;
         if (ret > 0) {
-            info(0, "AT2[%s]: Cannot set SMS message center, continuing", 
+            info(0, "AT2[%s]: Cannot set SMS message center, continuing",
                  octstr_get_cstr(privdata->name));
         }
     }
@@ -681,7 +681,7 @@
     ret = at2_send_modem_command(privdata, "AT+CSMS=?", 0, 0);
     if (ret != 0) {
         /* if it doesnt even understand the command, I'm sure it wont support it */
-        privdata->phase2plus = 0; 
+        privdata->phase2plus = 0;
     } else {
         /* we have to take a part a string like +CSMS: (0,1,128) */
         Octstr *ts;
@@ -736,7 +736,7 @@
 }
 
 
-static int at2_wait_modem_command(PrivAT2data *privdata, time_t timeout, int gt_flag, 
+static int at2_wait_modem_command(PrivAT2data *privdata, time_t timeout, int gt_flag,
                                   int *output)
 {
     Octstr *line = NULL;
@@ -758,7 +758,7 @@
     if (privdata->lines != NULL)
         octstr_destroy(privdata->lines);
     privdata->lines = octstr_create("");
-    
+
     smsc_number = octstr_create("");
     while (privdata->fd != -1 && time(&cur_time) <= end_time) {
         O_DESTROY(line);
@@ -788,21 +788,21 @@
             }
             if (octstr_search(line, octstr_imm("+CMS ERROR"), 0) != -1) {
                 int errcode;
-                error(0, "AT2[%s]: CMS ERROR: %s", octstr_get_cstr(privdata->name), 
+                error(0, "AT2[%s]: CMS ERROR: %s", octstr_get_cstr(privdata->name),
                       octstr_get_cstr(line));
                 if (sscanf(octstr_get_cstr(line), "+CMS ERROR: %d", &errcode) == 1)
-                    error(0, "AT2[%s]: CMS ERROR: %s (%d)", octstr_get_cstr(privdata->name), 
+                    error(0, "AT2[%s]: CMS ERROR: %s (%d)", octstr_get_cstr(privdata->name),
                           at2_error_string(errcode), errcode);
                 ret = 1;
                 goto end;
             }
-            if (octstr_search(line, octstr_imm("+CMTI:"), 0) != -1 || 
+            if (octstr_search(line, octstr_imm("+CMTI:"), 0) != -1 ||
                 octstr_search(line, octstr_imm("+CDSI:"), 0) != -1) {
-                /* 
+                /*
                  * we received an incoming message indication
-                 * put it in the pending_incoming_messages queue for later retrieval 
+                 * put it in the pending_incoming_messages queue for later retrieval
                  */
-                debug("bb.smsc.at2", 0, "AT2[%s]: +CMTI incoming SMS indication: %s", 
+                debug("bb.smsc.at2", 0, "AT2[%s]: +CMTI incoming SMS indication: %s",
                       octstr_get_cstr(privdata->name), octstr_get_cstr(line));
                 gwlist_append(privdata->pending_incoming_messages, line);
                 line = NULL;
@@ -814,7 +814,7 @@
                 line2 = at2_wait_line(privdata, 1, 0);
 
                 if (line2 == NULL) {
-                    error(0, "AT2[%s]: got +CMT but waiting for next line timed out", 
+                    error(0, "AT2[%s]: got +CMT but waiting for next line timed out",
                           octstr_get_cstr(privdata->name));
                 } else {
                     octstr_append_cstr(line, "\n");
@@ -822,7 +822,7 @@
                     O_DESTROY(line2);
                     at2_pdu_extract(privdata, &pdu, line, smsc_number);
                     if (pdu == NULL) {
-                        error(0, "AT2[%s]: got +CMT but pdu_extract failed", 
+                        error(0, "AT2[%s]: got +CMT but pdu_extract failed",
                               octstr_get_cstr(privdata->name));
                     } else {
                         /* count message even if I can't decode it */
@@ -852,13 +852,13 @@
                 continue;
             }
             if ((octstr_search(line, octstr_imm("+CMGS:"),0) != -1) && (output)) {
-                /* 
-                 * found response to a +CMGS command, read the message id 
-                 * and return it in output 
+                /*
+                 * found response to a +CMGS command, read the message id
+                 * and return it in output
                  */
                 long temp;
                 if (octstr_parse_long(&temp, line, octstr_search(line, octstr_imm("+CMGS:"), 0) + 6, 10) == -1)
-                    error(0, "AT2[%s]: Got +CMGS but failed to read message id", 
+                    error(0, "AT2[%s]: Got +CMGS but failed to read message id",
                           octstr_get_cstr(privdata->name));
                 else
                     *output = temp;
@@ -869,7 +869,7 @@
                 error(0, "AT2[%s]: Generic error: %s", octstr_get_cstr(privdata->name),
                       octstr_get_cstr(line));
                 if (sscanf(octstr_get_cstr(line), "ERROR: %d", &errcode) == 1)
-                    error(0, "AT2[%s]: Generic error: %s (%d)", octstr_get_cstr(privdata->name), 
+                    error(0, "AT2[%s]: Generic error: %s (%d)", octstr_get_cstr(privdata->name),
                           at2_error_string(errcode), errcode);
                 ret = -1;
                 goto end;
@@ -910,29 +910,29 @@
     /* read one message from memory */
     at2_write_line(privdata, cmd);
     if (at2_wait_modem_command(privdata, 0, 0, &message_count) != 0) {
-        debug("bb.smsc.at2", 0, "AT2[%s]: failed to get message %d.", 
+        debug("bb.smsc.at2", 0, "AT2[%s]: failed to get message %d.",
               octstr_get_cstr(privdata->name), message_number);
         return 0; /* failed to read the message - skip to next message */
     }
 
     /* no need to delete if no message collected */
-    if (!message_count) { 
-        debug("bb.smsc.at2", 0, "AT2[%s]: not deleted.", 
+    if (!message_count) {
+        debug("bb.smsc.at2", 0, "AT2[%s]: not deleted.",
               octstr_get_cstr(privdata->name));
         return 0;
     }
 
     sprintf(cmd, "AT+CMGD=%d", message_number); /* delete the message we just read */
-    /* 
+    /*
     * 3 seconds (default timeout of send_modem_command()) is not enough with some
-    * modems if the message is large, so we'll give it 7 seconds 
+    * modems if the message is large, so we'll give it 7 seconds
     */
-    if (at2_send_modem_command(privdata, cmd, 7, 0) != 0) {  
-        /* 
-         * failed to delete the message, we'll just ignore it for now, 
+    if (at2_send_modem_command(privdata, cmd, 7, 0) != 0) {
+        /*
+         * failed to delete the message, we'll just ignore it for now,
          * this is bad, since if the message really didn't get deleted
-         * we'll see it next time around. 
-         */                
+         * we'll see it next time around.
+         */
         error(2, "AT2[%s]: failed to delete message %d.",
               octstr_get_cstr(privdata->name), message_number);
     }
@@ -958,7 +958,7 @@
         int pos;
         long location;
         Octstr *cmti_storage = NULL, *line = NULL;
-        
+
         line = gwlist_extract_first(privdata->pending_incoming_messages);
         /* message memory starts after the first quote in the string */
         if ((pos = octstr_search_char(line, '"', 0)) != -1) {
@@ -974,10 +974,10 @@
         } else
             /* reset pos for the next lookup which would start from the beginning if no memory
              * location was found */
-            pos = 0; 
+            pos = 0;
 
         /* if no message storage is set in configuration - set now */
-        if (!privdata->modem->message_storage && cmti_storage) { 
+        if (!privdata->modem->message_storage && cmti_storage) {
             info(2, "AT2[%s]: CMTI received, but no message-storage is set in confiuration."
                  "setting now to <%s>", octstr_get_cstr(privdata->name), octstr_get_cstr(cmti_storage));
             privdata->modem->message_storage = octstr_duplicate(cmti_storage);
@@ -1009,18 +1009,18 @@
             current_storage = octstr_duplicate(cmti_storage);
             at2_set_message_storage(privdata, cmti_storage);
         }
-        
+
         if (!at2_read_delete_message(privdata, location)) {
             error(1, "AT2[%s]: CMTI notification received, but no message found in memory!",
                   octstr_get_cstr(privdata->name));
         }
-        
+
         octstr_destroy(line);
         octstr_destroy(cmti_storage);
     }
-    
+
     /* set prefered message storage back to what configured */
-    if (current_storage && privdata->modem->message_storage 
+    if (current_storage && privdata->modem->message_storage
         && (octstr_compare(privdata->modem->message_storage, current_storage) != 0))
         at2_set_message_storage(privdata, privdata->modem->message_storage);
 
@@ -1039,40 +1039,40 @@
     if (privdata->sms_memory_usage) {
         /*
          * that is - greater then 0, meaning there are some messages to fetch
-         * now - I used to just loop over the first input_mem_sms_used locations, 
-         * but it doesn't hold, since under load, messages may be received while 
-         * we're in the loop, and get stored in locations towards the end of the list, 
-         * thus creating 'holes' in the memory. 
-         * 
-         * There are two ways we can fix this : 
+         * now - I used to just loop over the first input_mem_sms_used locations,
+         * but it doesn't hold, since under load, messages may be received while
+         * we're in the loop, and get stored in locations towards the end of the list,
+         * thus creating 'holes' in the memory.
+         *
+         * There are two ways we can fix this :
          *   (a) Just read the last message location, delete it and return.
-         *       It's not a complete solution since holes can still be created if messages 
-         *       are received between the memory check and the delete command, 
+         *       It's not a complete solution since holes can still be created if messages
+         *       are received between the memory check and the delete command,
          *       and anyway - it will slow us down and won't hold well under pressure
-         *   (b) Just scan the entire memory each call, bottom to top. 
+         *   (b) Just scan the entire memory each call, bottom to top.
          *       This will be slow too, but it'll be reliable.
          *
          * We can massivly improve performance by stopping after input_mem_sms_used messages
-         * have been read, but send_modem_command returns 0 for no message as well as for a 
-         * message read, and the only other way to implement it is by doing memory_check 
-         * after each read and stoping when input_mem_sms_used get to 0. This is slow 
-         * (modem commands take time) so we improve speed only if there are less then 10 
+         * have been read, but send_modem_command returns 0 for no message as well as for a
+         * message read, and the only other way to implement it is by doing memory_check
+         * after each read and stoping when input_mem_sms_used get to 0. This is slow
+         * (modem commands take time) so we improve speed only if there are less then 10
          * messages in memory.
          *
-         * I implemented the alternative - changed at2_wait_modem_command to return the 
+         * I implemented the alternative - changed at2_wait_modem_command to return the
          * number of messages it collected.
          */
         int i;
         int message_count = 0; /* cound number of messages collected */
 
-        debug("bb.smsc.at2", 0, "AT2[%s]: %d messages waiting in memory", 
+        debug("bb.smsc.at2", 0, "AT2[%s]: %d messages waiting in memory",
               octstr_get_cstr(privdata->name), privdata->sms_memory_usage);
 
         /*
          * loop till end of memory or collected enouch messages
          */
         for (i = 1; i <= privdata->sms_memory_capacity &&
-             message_count < privdata->sms_memory_usage; ++i) { 
+             message_count < privdata->sms_memory_usage; ++i) {
 
             /* if (meanwhile) there are pending CMTI notifications, process these first
              * to not let CMTI and sim buffering sit in each others way */
@@ -1083,7 +1083,7 @@
             message_count += at2_read_delete_message(privdata, i);
         }
     }
-    
+
     /*
     at2_send_modem_command(privdata, ModemTypes[privdata->modemid].init1, 0, 0);
     */
@@ -1099,7 +1099,7 @@
     Octstr *search_cpms = NULL;
 
     /* select memory type and get report */
-    if ((ret = at2_send_modem_command(privdata, "AT+CPMS?", 0, 0)) != 0) { 
+    if ((ret = at2_send_modem_command(privdata, "AT+CPMS?", 0, 0)) != 0) {
         debug("bb.smsc.at2.memory_check", 0, "failed to send mem select command to modem %d", ret);
         return -1;
     }
@@ -1112,22 +1112,22 @@
         pos += 6; /* position of parser in the stream - start after header */
 
         /* skip memory indication */
-        pos = octstr_search(privdata->lines, octstr_imm(","), pos) + 1; 
+        pos = octstr_search(privdata->lines, octstr_imm(","), pos) + 1;
 
         /* find all the values */
         while (index < 4 && pos < octstr_len(privdata->lines) &&
-               (pos = octstr_parse_long(&values[index], privdata->lines, pos, 10)) != -1) { 
+               (pos = octstr_parse_long(&values[index], privdata->lines, pos, 10)) != -1) {
             ++pos; /* skip number seperator */
             ++index; /* increment array index */
             if (index == 2)
                 /* skip second memory indication */
-                pos = octstr_search(privdata->lines, octstr_imm(","), pos) + 1; 
+                pos = octstr_search(privdata->lines, octstr_imm(","), pos) + 1;
         }
 
-        if (index < 4) { 
+        if (index < 4) {
             /* didn't get all memory data - I don't why, so I'll bail */
             debug("bb.smsc.at2", 0, "AT2[%s]: couldn't parse all memory locations : %d:'%s'.",
-                  octstr_get_cstr(privdata->name), index, 
+                  octstr_get_cstr(privdata->name), index,
                   &(octstr_get_cstr(privdata->lines)[pos]));
             O_DESTROY(search_cpms);
             return -1;
@@ -1141,17 +1141,17 @@
         */
 
         /* everything's cool */
-        ret = 0; 
+        ret = 0;
 
         /*  clear the buffer */
         O_DESTROY(privdata->lines);
 
     } else {
-        debug("bb.smsc.at2", 0, "AT2[%s]: no correct header for CPMS response.", 
+        debug("bb.smsc.at2", 0, "AT2[%s]: no correct header for CPMS response.",
               octstr_get_cstr(privdata->name));
 
         /* didn't get a +CPMS response - this is clearly an error */
-        ret = -1; 
+        ret = -1;
     }
 
     O_DESTROY(search_cpms);
@@ -1234,7 +1234,7 @@
              speed = B9600;
 #endif
     }
-    
+
     cfsetospeed(&tios, speed);
     cfsetispeed(&tios, speed);
     ret = tcsetattr(privdata->fd, TCSANOW, &tios); /* apply changes now */
@@ -1280,12 +1280,12 @@
 
             info(0, "AT2[%s]: trying to use speed <%ld> from modem definition",
                  octstr_get_cstr(privdata->name), privdata->modem->speed);
-            if (at2_test_speed(privdata, privdata->modem->speed) == 0) { 
+            if (at2_test_speed(privdata, privdata->modem->speed) == 0) {
                 privdata->speed = privdata->modem->speed;
-            	info(0, "AT2[%s]: speed is %ld", 
+            	info(0, "AT2[%s]: speed is %ld",
                      octstr_get_cstr(privdata->name), privdata->speed);
             } else {
-                info(0, "AT2[%s]: speed in modem definition don't work, will autodetect", 
+                info(0, "AT2[%s]: speed in modem definition don't work, will autodetect",
                      octstr_get_cstr(privdata->name));
             }
         }
@@ -1301,20 +1301,20 @@
         }
 
         if (at2_open_device(privdata)) {
-            error(errno, "AT2[%s]: at2_device_thread: open_at2_device failed.", 
+            error(errno, "AT2[%s]: at2_device_thread: open_at2_device failed.",
                   octstr_get_cstr(privdata->name));
             reconnecting = 1;
             continue;
         }
 
         if (at2_login_device(privdata)) {
-            error(errno, "AT2[%s]: at2_device_thread: at2_login_device failed.", 
+            error(errno, "AT2[%s]: at2_device_thread: at2_login_device failed.",
                   octstr_get_cstr(privdata->name));
             reconnecting = 1;
             continue;
         }
 
-        if (privdata->max_error_count > 0 && error_count > privdata->max_error_count 
+        if (privdata->max_error_count > 0 && error_count > privdata->max_error_count
             && privdata->modem != NULL && privdata->modem->reset_string != NULL) {
             error_count = 0;
             if (at2_send_modem_command(privdata,
@@ -1363,7 +1363,7 @@
 
         if (privdata->keepalive &&
             idle_timeout + privdata->keepalive < time(NULL)) {
-            if (at2_send_modem_command(privdata, 
+            if (at2_send_modem_command(privdata,
                 octstr_get_cstr(privdata->modem->keepalive_cmd), 5, 0) < 0) {
                 at2_close_device(privdata);
                 reconnecting = 1;
@@ -1425,15 +1425,15 @@
           octstr_get_cstr(privdata->name),
           finish_sending ? "slow" : "instant");
 
-    /* 
+    /*
      * Documentation claims this would have been done by smscconn.c,
-     * but isn't when this code is being written. 
+     * but isn't when this code is being written.
      */
     conn->why_killed = SMSCCONN_KILLED_SHUTDOWN;
-    privdata->shutdown = 1; 
-    /* 
+    privdata->shutdown = 1;
+    /*
      * Separate from why_killed to avoid locking, as
-     * why_killed may be changed from outside? 
+     * why_killed may be changed from outside?
      */
     if (finish_sending == 0) {
         Msg *msg;
@@ -1469,7 +1469,7 @@
 
     if (conn->status == SMSCCONN_DISCONNECTED)
         conn->status = SMSCCONN_ACTIVE;
-    
+
     /* in case there are messages in the buffer already */
     gwthread_wakeup(privdata->device_thread);
     debug("smsc.at2", 0, "AT2[%s]: start called", octstr_get_cstr(privdata->name));
@@ -1504,7 +1504,7 @@
         error(0, "AT2[-]: 'device' missing in at2 configuration.");
         goto error;
     }
-    
+
     if (octstr_str_compare(privdata->device, "rawtcp") == 0) {
         privdata->rawtcp_host = cfg_get(cfg, octstr_imm("host"));
         if (privdata->rawtcp_host == NULL) {
@@ -1645,18 +1645,18 @@
     int tmp;
 	Octstr *numtmp;
 	Octstr *tmp2;
-	
+
     buffer = octstr_duplicate(line);
     /* find the beginning of a message from the modem*/
 
-    if ((pos = octstr_search(buffer, octstr_imm("+CDS:"), 0)) != -1) 
+    if ((pos = octstr_search(buffer, octstr_imm("+CDS:"), 0)) != -1)
         pos += 5;
     else {
         if ((pos = octstr_search(buffer, octstr_imm("+CMT:"), 0)) != -1)
             pos += 5;
         else if ((pos = octstr_search(buffer, octstr_imm("+CMGR:"), 0)) != -1) {
             /* skip status field in +CMGR response */
-            if ((pos = octstr_search(buffer, octstr_imm(","), pos + 6)) != -1) 
+            if ((pos = octstr_search(buffer, octstr_imm(","), pos + 6)) != -1)
                 pos++;
             else
                 goto nomsg;
@@ -1688,7 +1688,7 @@
               + at2_hexchar(octstr_get_char(buffer, pos + 1));
         if (tmp < 0)
             goto nomsg;
-       
+
         numtmp = octstr_create_from_data(octstr_get_cstr(buffer)+pos+2,tmp * 2);	/* we now have the hexchars of the SMSC in GSM encoding */
 		octstr_hex_to_binary(numtmp);
 		tmp2 = gsm2number(numtmp);
@@ -1740,7 +1740,7 @@
     len = octstr_len(pdu);
 	if(len<= 0)
 		return octstr_create("");
-		
+
     ton = octstr_get_char(pdu,pos++);
     npi = ton & 0x0F;
     ton =  (ton >> 4) & 0x07;
@@ -1766,7 +1766,7 @@
 	    c = octstr_get_char(pdu,pos++);
 		a =  c & 0x0F;
 		b =  ((c & 0xF0) >> 4);
-	
+
 		if((b == 0x0F) && (len < 2))
 		{
 			octstr_append_char(tmp, nibble2hex(a));
@@ -1824,9 +1824,9 @@
     long stime; /* time in seconds */
     int timezone; /* timezone in 15 minutes jumps from GMT */
 
-    /* 
+    /*
      * Note: some parts of the PDU are not decoded because they are
-     * not needed for the Msg type. 
+     * not needed for the Msg type.
      */
 
     /* convert the pdu to binary format for ease of processing */
@@ -1848,7 +1848,7 @@
         tmpstr = octstr_copy(pdu, 3, len);
         at2_decode7bituncompressed(tmpstr, (((len - 1) * 4 - 3) / 7) + 1, origin, 0);
         octstr_destroy(tmpstr);
-        debug("bb.smsc.at2", 0, "AT2[%s]: Alphanumeric sender <%s>", 
+        debug("bb.smsc.at2", 0, "AT2[%s]: Alphanumeric sender <%s>",
               octstr_get_cstr(privdata->name), octstr_get_cstr(origin));
         pos += (len + 1) / 2;
     } else {
@@ -1862,8 +1862,8 @@
             if (i + 1 < len)
                 octstr_append_char(origin, (octstr_get_char(pdu, pos) >> 4) + 48);
         }
-        debug("bb.smsc.at2", 0, "AT2[%s]: Numeric sender %s <%s>", 
-              octstr_get_cstr(privdata->name), ((ntype & 0x90) == 0x90 ? "(international)" : ""), 
+        debug("bb.smsc.at2", 0, "AT2[%s]: Numeric sender %s <%s>",
+              octstr_get_cstr(privdata->name), ((ntype & 0x90) == 0x90 ? "(international)" : ""),
               octstr_get_cstr(origin));
     }
 
@@ -1883,7 +1883,7 @@
     pos++;
     mtime.year += (mtime.year < 70 ? 2000 : 1900);
     mtime.month = swap_nibbles(octstr_get_char(pdu, pos));
-    mtime.month--;    
+    mtime.month--;
     pos++;
     mtime.day = swap_nibbles(octstr_get_char(pdu, pos));
     pos++;
@@ -1894,24 +1894,24 @@
     mtime.second = swap_nibbles(octstr_get_char(pdu, pos));
     pos++;
 
-    /* 
-     * time zone: 
+    /*
+     * time zone:
      *
-     * time zone is "swapped nibble", with the MSB as the sign (1 is negative).  
+     * time zone is "swapped nibble", with the MSB as the sign (1 is negative).
      */
     timezone = swap_nibbles(octstr_get_char(pdu, pos));
     pos++;
     timezone = ((timezone >> 7) ? -1 : 1) * (timezone & 127);
-    /* 
-     * Ok, that was the time zone as read from the PDU. Now how to interpert it? 
-     * All the handsets I tested send the timestamp of their local time and the 
-     * timezone as GMT+0. I assume that the timestamp is the handset's local time, 
-     * so we need to apply the timezone in reverse to get GM time: 
+    /*
+     * Ok, that was the time zone as read from the PDU. Now how to interpert it?
+     * All the handsets I tested send the timestamp of their local time and the
+     * timezone as GMT+0. I assume that the timestamp is the handset's local time,
+     * so we need to apply the timezone in reverse to get GM time:
      */
 
-    /* 
-     * time in PDU is handset's local time and timezone is handset's time zone 
-     * difference from GMT 
+    /*
+     * time in PDU is handset's local time and timezone is handset's time zone
+     * difference from GMT
      */
     mtime.hour -= timezone / 4;
     mtime.minute -= 15 * (timezone % 4);
@@ -1924,7 +1924,7 @@
     len = octstr_get_char(pdu, pos);
     pos++;
 
-    debug("bb.smsc.at2", 0, "AT2[%s]: User data length read as (%d)", 
+    debug("bb.smsc.at2", 0, "AT2[%s]: User data length read as (%d)",
           octstr_get_cstr(privdata->name), len);
 
     /* if there is a UDH */
@@ -1972,11 +1972,12 @@
              * Fix length because UDH data length is determined
              * in septets if we are in GSM coding, otherwise it's in octets. Adding 6
              * will ensure that for an octet length of 0, we get septet length 0,
-             * and for octet length 1 we get septet length 2. 
+             * and for octet length 1 we get septet length 2.
              */
             len = len + udhlen + 1 - (8 * (udhlen + 1) + 6) / 7;
         }
         at2_decode7bituncompressed(tmpstr, len, text, offset);
+        if(udhi) octstr_truncate(text, len-1); //FIX: fixed garbage characters in multi-part messages.
     }
 
     message->sms.sender = origin;
@@ -1997,7 +1998,7 @@
     octstr_destroy(tmpstr);
 
     return message;
-    
+
 msg_error:
     error(1, "AT2[%s]: Invalid DELIVER-SMS pdu!", octstr_get_cstr(privdata->name));
     O_DESTROY(udh);
@@ -2024,9 +2025,9 @@
     /* Message reference */
     tp_mr = octstr_get_char(pdu, 1);
     msg_id = octstr_format("%d", tp_mr);
-    debug("bb.smsc.at2", 0, "AT2[%s]: got STATUS-REPORT for message <%d>:", 
+    debug("bb.smsc.at2", 0, "AT2[%s]: got STATUS-REPORT for message <%d>:",
           octstr_get_cstr(privdata->name), tp_mr);
-    
+
     /* reciver address */
     len = octstr_get_char(pdu, 2);
     ntype = octstr_get_char(pdu, 3);
@@ -2095,10 +2096,10 @@
             tmpstr = octstr_create("Failed");
             break;
     }
-    /* Actually, the above implementation is not correct, as the reference 
-     * says that implementations should consider any "reserved" values to be 
-     * "failure", but most reserved values fall into one of the three 
-     * categories. It will catch "reserved" values where the first 3 MSBits 
+    /* Actually, the above implementation is not correct, as the reference
+     * says that implementations should consider any "reserved" values to be
+     * "failure", but most reserved values fall into one of the three
+     * categories. It will catch "reserved" values where the first 3 MSBits
      * are not set as "Success" which may not be correct. */
 
     if ((dlrmsg = dlr_find(privdata->conn->id, msg_id, receiver, type)) == NULL) {
@@ -2109,7 +2110,7 @@
 
     /* Beware DLR URL is now in msg->sms.dlr_url given by dlr_find() */
     dlrmsg->sms.msgdata = octstr_duplicate(tmpstr);
-	
+
 error:
     O_DESTROY(tmpstr);
     O_DESTROY(pdu);
@@ -2184,7 +2185,7 @@
 {
     Msg *msg;
 
-		if (privdata->modem->enable_mms && 
+		if (privdata->modem->enable_mms &&
 				gw_prioqueue_len(privdata->outgoing_queue) > 1)
             at2_send_modem_command(privdata, "AT+CMMS=2", 0, 0);
 
@@ -2204,11 +2205,11 @@
         msg->sms.sender = octstr_duplicate(privdata->my_number);
     }
 
-    /* 
-     * The standard says you should be prepending the PDU with 00 to indicate 
-     * to use the default SC. Some older modems dont expect this so it can be 
-     * disabled 
-     * NB: This extra padding is not counted in the CMGS byte count 
+    /*
+     * The standard says you should be prepending the PDU with 00 to indicate
+     * to use the default SC. Some older modems dont expect this so it can be
+     * disabled
+     * NB: This extra padding is not counted in the CMGS byte count
      */
     sc[0] = '\0';
 
@@ -2224,8 +2225,8 @@
         }
 
         int msg_id = -1;
-        /* 
-         * send the initial command and then wait for > 
+        /*
+         * send the initial command and then wait for >
          */
         sprintf(command, "AT+CMGS=%ld", octstr_len(pdu) / 2);
 
@@ -2235,10 +2236,10 @@
 
         if (ret == 1) {/* > only! */
 
-            /* 
-             * Ok the > has been see now so we can send the PDU now and a 
-             * control Z but no CR or LF 
-             * 
+            /*
+             * Ok the > has been see now so we can send the PDU now and a
+             * control Z but no CR or LF
+             *
              * We will handle the 'nokiaphone' types a bit differently, since
              * they have a generic error in accepting PDUs that are "too big".
              * Which means, PDU that are longer then 18 bytes get truncated by
@@ -2246,7 +2247,7 @@
              * All other types will get handled as used to be.
              */
 
-            if (octstr_compare(privdata->modem->id, octstr_imm("nokiaphone")) != 0) { 
+            if (octstr_compare(privdata->modem->id, octstr_imm("nokiaphone")) != 0) {
 
                 sprintf(command, "%s%s", sc, octstr_get_cstr(pdu));
                 at2_write(privdata, command);
@@ -2257,7 +2258,7 @@
                 /* include the CTRL-Z in the PDU string */
                 sprintf(command, "%s%s%c", sc, octstr_get_cstr(pdu), 0x1A);
 
-                /* chop PDU into 18-byte-at-a-time pieces to prevent choking 
+                /* chop PDU into 18-byte-at-a-time pieces to prevent choking
                  * of certain GSM Phones (e.g. Nokia 6310, 6230 etc.) */
                 if (strlen(command) > 18) {
                     char chop[20];
@@ -2277,7 +2278,7 @@
                 } else {
                     at2_write(privdata, command);
                 }
-            }               
+            }
 
             /* wait 20 secs for modem command */
             ret = at2_wait_modem_command(privdata, 20, 0, &msg_id);
@@ -2306,6 +2307,18 @@
                 bb_smscconn_sent(privdata->conn, msg, NULL);
             }
         }
+        //FIX: added send_failed call to the message sending function when error is received during handshake.
+        else {
+        	//ret != 1
+            error(0,"AT2[%s]: Error received, notifiying failure, "
+            		"sender: %s receiver: %s msgdata: %s udhdata: %s",
+                    octstr_get_cstr(privdata->name),
+                    octstr_get_cstr(msg->sms.sender), octstr_get_cstr(msg->sms.receiver),
+                    octstr_get_cstr(msg->sms.msgdata), octstr_get_cstr(msg->sms.udhdata));
+	    bb_smscconn_send_failed(privdata->conn, msg,
+				    SMSCCONN_FAILED_TEMPORARILY, octstr_create("ERROR"));
+        }
+        //END of FIX
         O_DESTROY(pdu);
     }
 }
@@ -2320,7 +2333,7 @@
     Octstr *pdu = NULL, *temp = NULL, *buffer = octstr_create("");
     int len, setvalidity = 0;
 
-    /* 
+    /*
      * message type SUBMIT , bit mapped :
      * bit7                            ..                                    bit0
      * TP-RP , TP-UDHI, TP-SRR, TP-VPF(4), TP-VPF(3), TP-RD, TP-MTI(1), TP-MTI(0)
@@ -2346,10 +2359,10 @@
     octstr_append_char(buffer, fields_to_dcs(msg, /* data coding scheme */
         (msg->sms.alt_dcs != -1 ? msg->sms.alt_dcs : privdata->conn->alt_dcs)));
 
-    /* 
+    /*
      * Validity-Period (TP-VP)
      * see GSM 03.40 section 9.2.3.12
-     * defaults to 24 hours = 167 if not set 
+     * defaults to 24 hours = 167 if not set
      */
     if (msg->sms.validity >= 0) {
         if (msg->sms.validity > 635040)
@@ -2371,7 +2384,7 @@
         if (msg->sms.validity < 5)
             setvalidity = 0;
     } else
-        setvalidity = (privdata->validityperiod != NULL ? 
+        setvalidity = (privdata->validityperiod != NULL ?
             atoi(octstr_get_cstr(privdata->validityperiod)) : 167);
 
     if (setvalidity >= 0 && setvalidity <= 143)
@@ -2403,7 +2416,7 @@
              * The reason we branch here is because UDH data length is determined
              * in septets if we are in GSM coding, otherwise it's in octets. Adding 6
              * will ensure that for an octet length of 0, we get septet length 0,
-             * and for octet length 1 we get septet length 2. 
+             * and for octet length 1 we get septet length 2.
              */
             int temp_len;
             len += (temp_len = (((8 * octstr_len(msg->sms.udhdata)) + 6) / 7));
@@ -2435,7 +2448,7 @@
         }
 
         charset_utf8_to_gsm(msg->sms.msgdata);
-        
+
         if ((temp = at2_encode7bituncompressed(msg->sms.msgdata, offset)) != NULL)
             octstr_append(buffer, temp);
         O_DESTROY(temp);
@@ -2467,7 +2480,7 @@
 
     /* start packing the septet stream into an octet stream */
     for (posS = 0, posT = 0; (source_chr = octstr_get_char(source, posS++)) != -1;) {
-        /* grab least significant bits from current septet and 
+        /* grab least significant bits from current septet and
          * store them packed to the right */
         target_chr |= (source_chr & LSBmask[i]) << iStore;
         /* store current byte if last command filled it */
@@ -2476,14 +2489,14 @@
             octstr_append_char(target, target_chr);
             target_chr = 0;
         }
-        /* grab most significant bits from current septet and 
+        /* grab most significant bits from current septet and
          * store them packed to the left */
         target_chr |= (source_chr & MSBmask[7 - i]) >> (8 - iStore) % 8;
         /* advance target bit index by 7 (modulo 8 addition ) */
         iStore = (--iStore < 0 ? 7 : iStore);
         /* if just finished packing 8 septets (into 7 octets) don't advance mask index */
-        if (iStore != 0) 
-            i = (++i > 7 ? 1 : i); 
+        if (iStore != 0)
+            i = (++i > 7 ? 1 : i);
     }
 
     /* don't forget to pack the leftovers ;-) */
@@ -2519,16 +2532,16 @@
 static int at2_detect_speed(PrivAT2data *privdata)
 {
     int i;
-    int autospeeds[] = { 
+    int autospeeds[] = {
 #ifdef B115200
 	115200,
 #endif
 #ifdef	B57600
-	57600, 
+	57600,
 #endif
 	38400, 19200, 9600 };
 
-    debug("bb.smsc.at2", 0, "AT2[%s]: detecting modem speed. ", 
+    debug("bb.smsc.at2", 0, "AT2[%s]: detecting modem speed. ",
           octstr_get_cstr(privdata->name));
 
     for (i = 0; i < (sizeof(autospeeds) / sizeof(int)); i++) {
@@ -2546,7 +2559,7 @@
 }
 
 
-static int at2_test_speed(PrivAT2data *privdata, long speed) 
+static int at2_test_speed(PrivAT2data *privdata, long speed)
 {
     int res;
 
@@ -2556,14 +2569,14 @@
     at2_read_buffer(privdata); /* give telnet escape sequences a chance */
     at2_set_speed(privdata, speed);
     /* send a return so the modem can detect the speed */
-    res = at2_send_modem_command(privdata, "", 1, 0); 
+    res = at2_send_modem_command(privdata, "", 1, 0);
     res = at2_send_modem_command(privdata, "AT", 0, 0);
 
     if (res != 0)
         res = at2_send_modem_command(privdata, "AT", 0, 0);
     if (res != 0)
         res = at2_send_modem_command(privdata, "AT", 0, 0);
-        
+
     at2_close_device(privdata);
 
     return res;
@@ -2583,7 +2596,7 @@
 
     at2_set_speed(privdata, privdata->speed);
     /* send a return so the modem can detect the speed */
-    res = at2_send_modem_command(privdata, "", 1, 0); 
+    res = at2_send_modem_command(privdata, "", 1, 0);
     res = at2_send_modem_command(privdata, "AT", 0, 0);
 
     if (at2_send_modem_command(privdata, "AT&F", 0, 0) == -1) {
@@ -2611,24 +2624,24 @@
             continue;
         }
 
-        /* 
-        debug("bb.smsc.at2",0,"AT2[%s]: searching for %s", octstr_get_cstr(privdata->name), 
-              octstr_get_cstr(modem->name)); 
+        /*
+        debug("bb.smsc.at2",0,"AT2[%s]: searching for %s", octstr_get_cstr(privdata->name),
+              octstr_get_cstr(modem->name));
         */
 
         if (octstr_search(privdata->lines, modem->detect_string, 0) != -1) {
             if (octstr_len(modem->detect_string2) == 0) {
-                debug("bb.smsc.at2", 0, "AT2[%s]: found string <%s>, using modem definition <%s>", 
-                      octstr_get_cstr(privdata->name), octstr_get_cstr(modem->detect_string), 
+                debug("bb.smsc.at2", 0, "AT2[%s]: found string <%s>, using modem definition <%s>",
+                      octstr_get_cstr(privdata->name), octstr_get_cstr(modem->detect_string),
                       octstr_get_cstr(modem->name));
                 privdata->modem = modem;
                 break;
             } else {
                 if (octstr_search(privdata->lines, modem->detect_string2, 0) != -1) {
                     debug("bb.smsc.at2", 0, "AT2[%s]: found string <%s> plus <%s>, using modem "
-                          "definition <%s>", octstr_get_cstr(privdata->name), 
-                          octstr_get_cstr(modem->detect_string), 
-                          octstr_get_cstr(modem->detect_string2), 
+                          "definition <%s>", octstr_get_cstr(privdata->name),
+                          octstr_get_cstr(modem->detect_string),
+                          octstr_get_cstr(modem->detect_string2),
                           octstr_get_cstr(modem->name));
                     privdata->modem = modem;
                     break;
@@ -2641,10 +2654,10 @@
     }
 
     if (privdata->modem == NULL) {
-        debug("bb.smsc.at2", 0, "AT2[%s]: Cannot detect modem, using generic", 
+        debug("bb.smsc.at2", 0, "AT2[%s]: Cannot detect modem, using generic",
               octstr_get_cstr(privdata->name));
         if ((modem = at2_read_modems(privdata, privdata->configfile, octstr_imm("generic"), 0)) == NULL) {
-            panic(0, "AT2[%s]: Cannot detect modem and generic not found", 
+            panic(0, "AT2[%s]: Cannot detect modem and generic not found",
                   octstr_get_cstr(privdata->name));
         } else {
             privdata->modem = modem;
@@ -2655,7 +2668,7 @@
     res = at2_send_modem_command(privdata, "AT+CSMS=?", 0, 0);
     if (res != 0)
         /* if it doesnt even understand the command, I'm sure it won't support it */
-        privdata->phase2plus = 0; 
+        privdata->phase2plus = 0;
     else {
         /* we have to take a part a string like +CSMS: (0,1,128) */
         Octstr *ts;
@@ -2696,14 +2709,14 @@
     ModemDef *modem;
     int i = 1;
 
-    /* 
-     * Use id and idnumber=0 or id=NULL and idnumber > 0 
+    /*
+     * Use id and idnumber=0 or id=NULL and idnumber > 0
      */
     if (octstr_len(id) == 0 && idnumber == 0)
         return NULL;
 
     if (idnumber == 0)
-        debug("bb.smsc.at2", 0, "AT2[%s]: Reading modem definitions from <%s>", 
+        debug("bb.smsc.at2", 0, "AT2[%s]: Reading modem definitions from <%s>",
               octstr_get_cstr(privdata->name), octstr_get_cstr(file));
     cfg = cfg_create(file);
 
@@ -2712,7 +2725,7 @@
 
     grplist = cfg_get_multi_group(cfg, octstr_imm("modems"));
     if (idnumber == 0)
-        debug("bb.smsc.at2", 0, "AT2[%s]: Found <%ld> modems in config", 
+        debug("bb.smsc.at2", 0, "AT2[%s]: Found <%ld> modems in config",
               octstr_get_cstr(privdata->name), gwlist_len(grplist));
 
     if (grplist == NULL)
@@ -2784,7 +2797,7 @@
 
         cfg_get_bool(&modem->enable_mms, grp, octstr_imm("enable-mms"));
 
-        /*	
+        /*
         if (modem->message_storage == NULL)
             modem->message_storage = octstr_create("SM");
         */
@@ -2872,7 +2885,7 @@
     }
 
     O_DESTROY(temp);
-    return out;	
+    return out;
 }
limit_sms_retry_in_long_msgs.patch (application/octet-stream, 17.5 KB)
Index: bb_smscconn.c
===================================================================
RCS file: /home/cvs/gateway/gw/bb_smscconn.c,v
retrieving revision 1.100
diff -u -r1.100 bb_smscconn.c
--- bb_smscconn.c	14 Jan 2009 11:11:46 -0000	1.100
+++ bb_smscconn.c	30 Apr 2009 09:32:37 -0000
@@ -1,58 +1,58 @@
-/* ==================================================================== 
- * The Kannel Software License, Version 1.0 
- * 
- * Copyright (c) 2001-2009 Kannel Group  
- * Copyright (c) 1998-2001 WapIT Ltd.   
- * All rights reserved. 
- * 
- * Redistribution and use in source and binary forms, with or without 
- * modification, are permitted provided that the following conditions 
- * are met: 
- * 
- * 1. Redistributions of source code must retain the above copyright 
- *    notice, this list of conditions and the following disclaimer. 
- * 
- * 2. Redistributions in binary form must reproduce the above copyright 
- *    notice, this list of conditions and the following disclaimer in 
- *    the documentation and/or other materials provided with the 
- *    distribution. 
- * 
- * 3. The end-user documentation included with the redistribution, 
- *    if any, must include the following acknowledgment: 
- *       "This product includes software developed by the 
- *        Kannel Group (http://www.kannel.org/)." 
- *    Alternately, this acknowledgment may appear in the software itself, 
- *    if and wherever such third-party acknowledgments normally appear. 
- * 
- * 4. The names "Kannel" and "Kannel Group" must not be used to 
- *    endorse or promote products derived from this software without 
- *    prior written permission. For written permission, please  
- *    contact [email protected]. 
- * 
- * 5. Products derived from this software may not be called "Kannel", 
- *    nor may "Kannel" appear in their name, without prior written 
- *    permission of the Kannel Group. 
- * 
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
- * DISCLAIMED.  IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS 
- * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,  
- * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT  
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR  
- * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,  
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE  
- * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,  
- * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
- * ==================================================================== 
- * 
- * This software consists of voluntary contributions made by many 
- * individuals on behalf of the Kannel Group.  For more information on  
- * the Kannel Group, please see <http://www.kannel.org/>. 
- * 
- * Portions of this software are based upon software originally written at  
- * WapIT Ltd., Helsinki, Finland for the Kannel project.  
- */ 
+/* ====================================================================
+ * The Kannel Software License, Version 1.0
+ *
+ * Copyright (c) 2001-2009 Kannel Group
+ * Copyright (c) 1998-2001 WapIT Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ *    if any, must include the following acknowledgment:
+ *       "This product includes software developed by the
+ *        Kannel Group (http://www.kannel.org/)."
+ *    Alternately, this acknowledgment may appear in the software itself,
+ *    if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Kannel" and "Kannel Group" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please
+ *    contact [email protected].
+ *
+ * 5. Products derived from this software may not be called "Kannel",
+ *    nor may "Kannel" appear in their name, without prior written
+ *    permission of the Kannel Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Kannel Group.  For more information on
+ * the Kannel Group, please see <http://www.kannel.org/>.
+ *
+ * Portions of this software are based upon software originally written at
+ * WapIT Ltd., Helsinki, Finland for the Kannel project.
+ */
 
 /*
  * SMSC Connection interface for Bearerbox.
@@ -65,7 +65,7 @@
  * Kalle Marjola 2000 for project Kannel
  * Alexander Malysh <amalysh at kannel.org> 2003, 2004, 2005
  */
- 
+
 #include "gw-config.h"
 
 #include <errno.h>
@@ -193,7 +193,7 @@
 static void handle_split(SMSCConn *conn, Msg *msg, long reason)
 {
     struct split_parts *split = msg->sms.split_parts;
-    
+
     /*
      * If temporarely failed, try again immediately but only if connection active.
      * Because if connection is not active we will loop for ever here consuming 100% CPU
@@ -205,7 +205,7 @@
         msg_destroy(msg);
         return;
     }
-    
+
     /*
      * if the reason is not a success and status is still success
      * then set status of a split to the reason.
@@ -233,7 +233,7 @@
      * we will split it again in smscconn_send(...).
      */
     msg_destroy(msg);
-        
+
     if (counter_decrease(split->parts_left) <= 1) {
         /* all splited parts were processed */
         counter_destroy(split->parts_left);
@@ -257,7 +257,7 @@
         octstr_destroy(reply);
         return;
     }
-    
+
     counter_increase(outgoing_sms_counter);
     load_increase(outgoing_sms_load);
     if (conn) counter_increase(conn->sent);
@@ -289,12 +289,13 @@
 
 void bb_smscconn_send_failed(SMSCConn *conn, Msg *sms, int reason, Octstr *reply)
 {
-    if (sms->sms.split_parts != NULL) {
+	//dded  && reason != SMSCCONN_FAILED_TEMPORARILY, otherwise msg stays in queue forever and overloads smsc
+    if (sms->sms.split_parts != NULL && reason != SMSCCONN_FAILED_TEMPORARILY) {
         handle_split(conn, sms, reason);
         octstr_destroy(reply);
         return;
     }
-    
+
     switch (reason) {
     case SMSCCONN_FAILED_TEMPORARILY:
         /*
@@ -309,7 +310,7 @@
              */
            if (sms_resend_retry >= 0 && sms->sms.resend_try >= sms_resend_retry) {
                warning(0, "Maximum retries for message exceeded, discarding it!");
-               bb_smscconn_send_failed(NULL, sms, SMSCCONN_FAILED_DISCARDED, 
+               bb_smscconn_send_failed(NULL, sms, SMSCCONN_FAILED_DISCARDED,
                                        octstr_create("Retries Exceeded"));
                break;
            }
@@ -318,7 +319,7 @@
        }
        gwlist_produce(outgoing_sms, sms);
        break;
-       
+
     case SMSCCONN_FAILED_SHUTDOWN:
         gwlist_produce(outgoing_sms, sms);
         break;
@@ -388,7 +389,7 @@
         msg_destroy(sms);
         return SMSCCONN_FAILED_REJECTED;
     }
-    
+
     if (black_list &&
 	numhash_find_number(black_list, sms->sms.sender) == 1) {
 	info(0, "Number <%s> is in black-list, message discarded",
@@ -439,7 +440,7 @@
                 msg_destroy(sms);
                 return SMSCCONN_SUCCESS;
             case concat_complete:
-                /* Combined sms received! save new one since it is now combined. */ 
+                /* Combined sms received! save new one since it is now combined. */
                 msg_destroy(sms);
                 /* Change the sms. */
                 sms = msg_duplicate(copy);
@@ -463,7 +464,7 @@
          */
         rc = route_incoming_to_boxc(copy);
     }
-    
+
     if (rc == -1 || (rc != SMSCCONN_SUCCESS && rc != SMSCCONN_QUEUED)) {
         warning(0, "incoming messages queue too long, dropping a message");
         if (sms->sms.sms_type == report_mo)
@@ -597,7 +598,7 @@
 
     /* create split sms counter */
     split_msg_counter = counter_create();
-    
+
     /* create smsc list and rwlock for it */
     smsc_list = gwlist_create();
     gw_rwlock_init_static(&smsc_list_lock);
@@ -610,7 +611,7 @@
     white_list_url = cfg_get(grp, octstr_imm("white-list"));
     if (white_list_url != NULL) {
         if ((white_list = numhash_create(octstr_get_cstr(white_list_url))) == NULL)
-            panic(0, "Could not get white-list at URL <%s>", 
+            panic(0, "Could not get white-list at URL <%s>",
                   octstr_get_cstr(white_list_url));
     }
     if ((os = cfg_get(grp, octstr_imm("white-list-regex"))) != NULL) {
@@ -618,11 +619,11 @@
             panic(0, "Could not compile pattern '%s'", octstr_get_cstr(os));
         octstr_destroy(os);
     }
-    
+
     black_list_url = cfg_get(grp, octstr_imm("black-list"));
     if (black_list_url != NULL) {
         if ((black_list = numhash_create(octstr_get_cstr(black_list_url))) == NULL)
-            panic(0, "Could not get black-list at URL <%s>", 
+            panic(0, "Could not get black-list at URL <%s>",
                   octstr_get_cstr(black_list_url));
     }
     if ((os = cfg_get(grp, octstr_imm("black-list-regex"))) != NULL) {
@@ -636,7 +637,7 @@
         sms_resend_frequency = 60;
     }
     info(0, "Set SMS resend frequency to %ld seconds.", sms_resend_frequency);
-            
+
     if (cfg_get_integer(&sms_resend_retry, grp, octstr_imm("sms-resend-retry")) == -1) {
         sms_resend_retry = -1;
         info(0, "SMS resend retry set to unlimited.");
@@ -659,18 +660,18 @@
 
     smsc_groups = cfg_get_multi_group(cfg, octstr_imm("smsc"));
     gwlist_add_producer(smsc_list);
-    for (i = 0; i < gwlist_len(smsc_groups) && 
+    for (i = 0; i < gwlist_len(smsc_groups) &&
         (grp = gwlist_get(smsc_groups, i)) != NULL; i++) {
-        conn = smscconn_create(grp, 1); 
+        conn = smscconn_create(grp, 1);
         if (conn == NULL)
             panic(0, "Cannot start with SMSC connection failing");
         gwlist_append(smsc_list, conn);
     }
     gwlist_remove_producer(smsc_list);
-    
+
     if ((router_thread = gwthread_create(sms_router, NULL)) == -1)
 	panic(0, "Failed to start a new thread for SMS routing");
-    
+
     gwlist_add_producer(incoming_sms);
     smsc_running = 1;
     return 0;
@@ -749,7 +750,7 @@
         /* find the group with equal smsc id */
         hit = 0;
         grp = NULL;
-        for (group_index = 0; group_index < gwlist_len(smsc_groups) && 
+        for (group_index = 0; group_index < gwlist_len(smsc_groups) &&
              (grp = gwlist_get(smsc_groups, group_index)) != NULL; group_index++) {
             smscid = cfg_get(grp, octstr_imm("smsc-id"));
             if (smscid != NULL && octstr_compare(smscid, id) == 0) {
@@ -767,7 +768,7 @@
             error(0, "HTTP: Could not find config for smsc-id `%s'", octstr_get_cstr(id));
             break;
         }
-        
+
         info(0,"HTTP: Re-starting smsc-id `%s'", octstr_get_cstr(id));
 
         new_conn = smscconn_create(grp, 1);
@@ -775,7 +776,7 @@
             error(0, "Start of SMSC connection failed, smsc-id `%s'", octstr_get_cstr(id));
             continue; /* keep old connection on the list */
         }
-        
+
         /* drop old connection from the active smsc list */
         gwlist_delete(smsc_list, i, 1);
         /* destroy the connection */
@@ -785,11 +786,11 @@
         num++;
     }
     gw_rwlock_unlock(&smsc_list_lock);
-    
+
     /* wake-up the router */
     if (router_thread >= 0)
         gwthread_wakeup(router_thread);
-    
+
     return 0;
 }
 
@@ -807,7 +808,7 @@
         smscconn_start(conn);
     }
     gw_rwlock_unlock(&smsc_list_lock);
-    
+
     if (router_thread >= 0)
         gwthread_wakeup(router_thread);
 }
@@ -874,7 +875,7 @@
         return;
 
     debug("smscconn", 0, "final clean-up for SMSCConn");
-    
+
     gw_rwlock_wrlock(&smsc_list_lock);
     for (i = 0; i < gwlist_len(smsc_list); i++) {
         conn = gwlist_get(smsc_list, i);
@@ -884,7 +885,7 @@
     smsc_list = NULL;
     gw_rwlock_unlock(&smsc_list_lock);
     gwlist_destroy(smsc_groups, NULL);
-    octstr_destroy(unified_prefix);    
+    octstr_destroy(unified_prefix);
     numhash_destroy(white_list);
     numhash_destroy(black_list);
     octstr_destroy(white_list_url);
@@ -940,8 +941,8 @@
         conn = gwlist_get(smsc_list, i);
 
         if ((smscconn_info(conn, &info) == -1)) {
-            /* 
-             * we do not delete SMSCs from the list 
+            /*
+             * we do not delete SMSCs from the list
              * this way we can show in the status which links are dead
              */
             continue;
@@ -959,7 +960,7 @@
             octstr_append_cstr(tmp, "    ");
             octstr_append(tmp, conn_id);
             octstr_append_cstr(tmp, "    ");
-        } 
+        }
         if (status_type == BBSTATUS_XML) {
             octstr_append_cstr(tmp, "<smsc>\n\t\t<name>");
             octstr_append(tmp, conn_name);
@@ -990,7 +991,7 @@
             default:
                 sprintf(tmp3, "unknown");
         }
-	
+
         if (status_type == BBSTATUS_XML)
             octstr_format_append(tmp, "<status>%s</status>\n\t\t<received>%ld</received>"
                 "\n\t\t<sent>%ld</sent>\n\t\t<failed>%ld</failed>\n\t\t"
@@ -1165,11 +1166,11 @@
 static long route_incoming_to_smsc(SMSCConn *conn, Msg *msg)
 {
     Octstr *smsc;
-    
+
     /* sanity check */
     if (!conn || !msg)
         return -1;
-        
+
     /* check for dlr rerouting */
     if (!conn->reroute_dlr && (msg->sms.sms_type == report_mo || msg->sms.sms_type == report_mt))
         return -1;
@@ -1186,7 +1187,7 @@
         /* drop into outbound queue again for routing */
         return smsc2_rout(msg, 0);
     }
-    
+
     if (conn->reroute_to_smsc) {
         /* change message direction */
         store_save_ack(msg, ack_success);
@@ -1197,7 +1198,7 @@
         msg->sms.smsc_id = octstr_duplicate(conn->reroute_to_smsc);
         return smsc2_rout(msg, 0);
     }
-    
+
     if (conn->reroute_by_receiver && msg->sms.receiver &&
                  (smsc = dict_get(conn->reroute_by_receiver, msg->sms.receiver))) {
         /* change message direction */
@@ -1211,7 +1212,7 @@
         return smsc2_rout(msg, 0);
     }
 
-    return -1; 
+    return -1;
 }
 
 
@@ -1254,7 +1255,7 @@
 {
     if (incoming_concat_msgs != NULL) /* already initialised? */
         return;
-    incoming_concat_msgs = dict_create(max_incoming_sms_qlength > 0 ? max_incoming_sms_qlength : 1024, 
+    incoming_concat_msgs = dict_create(max_incoming_sms_qlength > 0 ? max_incoming_sms_qlength : 1024,
                                        destroy_concatMsg);
     concat_lock = mutex_create();
     debug("bb.sms",0,"smsbox MO concatenated message handling enabled");
@@ -1397,9 +1398,9 @@
 
     debug("bb.sms.splits", 0, "Got part %d [ref %d, total parts %d] of message from %s. Dump follows:",
           part, refnum,totalparts, octstr_get_cstr(msg->sms.sender));
-     
+
     msg_dump(msg,0);
-     
+
     key = octstr_format("%S %S %S %d", msg->sms.sender, msg->sms.receiver, smscid, refnum);
     mutex_lock(concat_lock);
     if ((cmsg = dict_get(incoming_concat_msgs, key)) == NULL) {
@@ -1429,11 +1430,11 @@
     }
 
     /* check if we have seen message part before... */
-    if (cmsg->parts[part - 1] != NULL) {	  
+    if (cmsg->parts[part - 1] != NULL) {
         warning(0, "Duplicate message part %d, ref %d, from %s, to %s. Discarded!",
                 part, refnum, octstr_get_cstr(msg->sms.sender), octstr_get_cstr(msg->sms.receiver));
         store_save_ack(msg, ack_success);
-        msg_destroy(msg); 
+        msg_destroy(msg);
         *pmsg = msg = NULL;
     } else {
         cmsg->parts[part -1] = msg;
@@ -1459,12 +1460,12 @@
         octstr_append(msg->sms.msgdata, cmsg->parts[i]->sms.msgdata);
 
     /* Attempt to save the new one, if that fails, then reply with fail. */
-    if (store_save(msg) == -1) {	  
+    if (store_save(msg) == -1) {
         mutex_unlock(concat_lock);
         msg_destroy(msg);
         *pmsg = msg = NULL;
         return concat_error;
-    } else 
+    } else
         *pmsg = msg; /* return the message part. */
 
     /* Delete it from the queue and from the Dict. */
@@ -1498,7 +1499,7 @@
 {
     numhash_destroy(white_list);
     numhash_destroy(black_list);
-    
+
     if (white_list_url != NULL) {
         white_list = numhash_create(octstr_get_cstr(white_list_url));
     }
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.