[PATCH] regex volume 2

"David Schmitz" <[email protected]>
Newsgroups gmane.comp.mobile.kannel.devel
Message-ID <[email protected]>
Hi,

attached is a patch implementing some new configuration parameters based 
on POSIX regular expressions. All new parameters can be used in parallel 
with the non-regex-parameters. More elaborate documentation is included 
in the userdoc-part of the patch.

Awaiting votes and comments, please.

Regards,
David

PS:
Modified files:
gwlib/cfg.def
gwlib/regex.[ch]
gw/wap_push_ppg_pushuser.c
gw/urltrans.[ch]
gw/smscconn.c
gw/smsbox.c
gw/bb_smscconn.c 
gw/smscconn_p.h
doc/userguide/userguide.xml

The following params are new:
Group: core
white-list-regex
black-list-regex
----
Group: wap-push-user
allowed-prefix-regex
denied-prefix-regex
white-list-regex
black-list-regex
----
Group: smsbox
white-list-regex
black-list-regex
----
Group: smsc
allowed-smsc-id-regex
denied-smsc-id-regex
preferred-smsc-id-regex
allowed-prefix-regex
denied-prefix-regex
preferred-prefix-regex
----
Group: sms-service
accepted-smsc-regex
allowed-prefix-regex
denied-prefix-regex
allowed-receiver-prefix-regex
denied-receiver-prefix-regex
white-list-regex
black-list-regex
keyword-regex
----
Group: sendsms-user
allowed-prefix-regex
denied-prefix-regex
white-list-regex
black-list-regex

-- 

Mit freundlichen Gruessen/Best regards

David Schmitz
Softwareentwicklung

-----------------------------------------------------------------
Wapme Systems AG
Vogelsanger Weg 80
40470 Düsseldorf

Tel.:  + 49 -211-7 48 45 - 2708
Fax:  + 49 -211-80-6-06-2801

E-Mail:   [email protected]
Internet: http://www.wapme-systems.de
regex.patch (text/plain, 67.2 KB)
Index: gwlib/cfg.def
===================================================================
RCS file: /home/cvs/gateway/gwlib/cfg.def,v
retrieving revision 1.100
diff -u -b -B -r1.100 cfg.def
--- gwlib/cfg.def	14 Jan 2004 13:55:32 -0000	1.100
+++ gwlib/cfg.def	20 Jan 2004 15:01:51 -0000
@@ -100,7 +100,9 @@
     OCTSTR(store-file)
     OCTSTR(unified-prefix)
     OCTSTR(white-list)
+    OCTSTR(white-list-regex)
     OCTSTR(black-list)
+    OCTSTR(black-list-regex)
     OCTSTR(http-proxy-host)
     OCTSTR(http-proxy-port)
     OCTSTR(http-proxy-exceptions)
@@ -199,8 +201,12 @@
     OCTSTR(ppg-password)
     OCTSTR(country-prefix)
     OCTSTR(allowed-prefix)
+    OCTSTR(allowed-prefix-regex)
     OCTSTR(denied-prefix)
+    OCTSTR(denied-prefix-regex)
     OCTSTR(white-list)
+    OCTSTR(white-list-regex)
+    OCTSTR(black-list-regex)
     OCTSTR(black-list)
     OCTSTR(deny-ip)
     OCTSTR(allow-ip)
@@ -257,6 +263,8 @@
     OCTSTR(xmlrpc-url)
     OCTSTR(http-request-retry)
     OCTSTR(http-queue-delay)
+    OCTSTR(white-list-regex)
+    OCTSTR(black-list-regex)
 )
 
 
@@ -343,6 +351,12 @@
     OCTSTR(msg-id-type)
     OCTSTR(no-dlr)
     OCTSTR(connection-timeout)
+    OCTSTR(allowed-smsc-id-regex)
+    OCTSTR(denied-smsc-id-regex)
+    OCTSTR(preferred-smsc-id-regex)
+    OCTSTR(allowed-prefix-regex)
+    OCTSTR(denied-prefix-regex)
+    OCTSTR(preferred-prefix-regex)
     OCTSTR(max-error-count)
 )
 
@@ -359,6 +373,7 @@
     OCTSTR(text)
     OCTSTR(exec)
     OCTSTR(accepted-smsc)
+    OCTSTR(accepted-smsc-regex)
     OCTSTR(forced-smsc)
     OCTSTR(default-smsc)
     OCTSTR(faked-sender)
@@ -377,12 +392,19 @@
     OCTSTR(send-sender)
     OCTSTR(catch-all)
     OCTSTR(allowed-prefix)
+    OCTSTR(allowed-prefix-regex)
     OCTSTR(denied-prefix)
+    OCTSTR(denied-prefix-regex)
     OCTSTR(allowed-receiver-prefix)
+    OCTSTR(allowed-receiver-prefix-regex)
     OCTSTR(denied-receiver-prefix)
+    OCTSTR(denied-receiver-prefix-regex)
     OCTSTR(white-list)
+    OCTSTR(white-list-regex)
     OCTSTR(black-list)
+    OCTSTR(black-list-regex)
     OCTSTR(dlr-url)
+    OCTSTR(keyword-regex)
 )
 
 
@@ -408,6 +430,10 @@
     OCTSTR(white-list)
     OCTSTR(black-list)
     OCTSTR(dlr-url)
+    OCTSTR(allowed-prefix-regex)
+    OCTSTR(denied-prefix-regex)
+    OCTSTR(white-list-regex)
+    OCTSTR(black-list-regex)
 )
 
 
Index: gwlib/regex.c
===================================================================
RCS file: /home/cvs/gateway/gwlib/regex.c,v
retrieving revision 1.5
diff -u -b -B -r1.5 regex.c
--- gwlib/regex.c	15 Nov 2003 13:14:23 -0000	1.5
+++ gwlib/regex.c	20 Jan 2004 15:01:51 -0000
@@ -342,6 +340,17 @@
     
     return result;
 }
+
+int gw_regex_matches(const regex_t *preg, const Octstr *os) {
+    size_t n = 1;
+    regmatch_t p[10];
+
+    gw_assert(os != NULL && preg != NULL);
+    
+    debug("", 0, "exec regex on string: %s", octstr_get_cstr(os));
+    return
+        (gw_regex_exec(preg, os, n, p, 0) == 0) ? MATCH : NO_MATCH;
+};
 
 #endif  /* HAVE_REGEX || HAVE_PCRE */
 
Index: gwlib/regex.h
===================================================================
RCS file: /home/cvs/gateway/gwlib/regex.h,v
retrieving revision 1.4
diff -u -b -B -r1.4 regex.h
--- gwlib/regex.h	15 Nov 2003 13:14:23 -0000	1.4
+++ gwlib/regex.h	20 Jan 2004 15:01:51 -0000
@@ -85,6 +85,11 @@
  */
 #define REGEX_MAX_SUB_MATCH 10
 
+enum {
+    MATCH = 1,
+    NO_MATCH = -1
+};
+
 
 /*
  * Destroy a previously compiled regular expression.
@@ -187,6 +192,12 @@
 #define gw_regex_subst_pre(preg, os, rule) \
     gw_regex_subst_pre_real(preg, os, rule, __FILE__, __LINE__, __func__)
 
+
+/*
+ * checks whether or not a given Oct-string matches the regular expression.
+ * returns MATCH on match otherwise NO_MATCH
+ */
+int gw_regex_matches(const regex_t *preg, const Octstr *os);
 
 #endif
 #endif  /* REGEX_H */
Index: gw/wap_push_ppg_pushuser.c
===================================================================
RCS file: /home/cvs/gateway/gw/wap_push_ppg_pushuser.c,v
retrieving revision 1.16
diff -u -b -B -r1.16 wap_push_ppg_pushuser.c
--- gw/wap_push_ppg_pushuser.c	15 Nov 2003 13:14:23 -0000	1.16
+++ gw/wap_push_ppg_pushuser.c	20 Jan 2004 15:01:51 -0000
@@ -62,6 +62,7 @@
 
 #include "wap_push_ppg_pushuser.h"
 #include "numhash.h"
+#include "gwlib/regex.h"
 
 /***************************************************************************
  *
@@ -77,10 +78,17 @@
     Octstr *country_prefix;
     Octstr *allowed_prefix;            /* phone number prefixes allowed by 
                                           this user when pushing*/
+    regex_t *allowed_prefix_regex;
+    
     Octstr *denied_prefix;             /* and denied ones */
+    regex_t *denied_prefix_regex;
+
     Numhash *white_list;               /* phone numbers of this user, used for 
                                           push*/
+    regex_t *white_list_regex;
     Numhash *black_list;               /* numbers should not be used for push*/
+    regex_t *black_list_regex;
+
     Octstr *user_deny_ip;              /* this user allows pushes from these 
                                           IPs*/
     Octstr *user_allow_ip;             /* and denies them from these*/
@@ -456,9 +464,13 @@
     u->name = NULL;
     u->username = NULL;                  
     u->allowed_prefix = NULL;           
+    u->allowed_prefix_regex = NULL;           
     u->denied_prefix = NULL;             
+    u->denied_prefix_regex = NULL;             
     u->white_list = NULL;               
+    u->white_list_regex = NULL;               
     u->black_list = NULL;              
+    u->black_list_regex = NULL;              
     u->user_deny_ip = NULL;              
     u->user_allow_ip = NULL;
     u->smsc_id = NULL;
@@ -510,6 +522,27 @@
 	    octstr_destroy(os);
     }
 
+    if ((os = cfg_get(grp, octstr_imm("allowed-prefix-regex"))) != NULL) {
+        if ((u->allowed_prefix_regex = gw_regex_comp(os, REG_EXTENDED)) == NULL)
+            panic(0, "Could not compile pattern '%s'", octstr_get_cstr(os));
+        octstr_destroy(os);
+    };
+    if ((os = cfg_get(grp, octstr_imm("denied-prefix-regex"))) != NULL) {
+        if ((u->denied_prefix_regex = gw_regex_comp(os, REG_EXTENDED)) == NULL)
+            panic(0, "Could not compile pattern '%s'", octstr_get_cstr(os));
+        octstr_destroy(os);
+    };
+    if ((os = cfg_get(grp, octstr_imm("white-list-regex"))) != NULL) {
+        if ((u->white_list_regex = gw_regex_comp(os, REG_EXTENDED)) == NULL)
+            panic(0, "Could not compile pattern '%s'", octstr_get_cstr(os));
+        octstr_destroy(os);
+    };
+    if ((os = cfg_get(grp, octstr_imm("black-list-regex"))) != NULL) {
+        if ((u->black_list_regex = gw_regex_comp(os, REG_EXTENDED)) == NULL)
+            panic(0, "Could not compile pattern '%s'", octstr_get_cstr(os));
+        octstr_destroy(os);
+    };
+
     octstr_destroy(grpname);
     return u;
 
@@ -543,6 +576,11 @@
      octstr_destroy(u->user_allow_ip);
      octstr_destroy(u->smsc_id);
      octstr_destroy(u->default_smsc_id);
+
+     if (u->black_list_regex != NULL) gw_regex_destroy(u->black_list_regex);
+     if (u->white_list_regex != NULL) gw_regex_destroy(u->white_list_regex);
+     if (u->denied_prefix_regex != NULL) gw_regex_destroy(u->denied_prefix_regex);
+     if (u->allowed_prefix_regex != NULL) gw_regex_destroy(u->allowed_prefix_regex);
      gw_free(u);             
 }
 
@@ -854,7 +892,8 @@
     if (u == NULL)
         goto no_user;
 
-    if (u->allowed_prefix == NULL && u->denied_prefix == NULL)
+    if (        u->allowed_prefix == NULL && u->denied_prefix == NULL 
+        && u->allowed_prefix_regex == NULL && u->denied_prefix_regex == NULL)
         goto no_configuration;
 
     if (u->denied_prefix != NULL) {
@@ -870,10 +909,15 @@
         }
     }
 
-    if (u->allowed_prefix == NULL) {
+    /* note: country-prefix _must_be included in the pattern */
+    if (u->denied_prefix_regex != NULL) 
+        if (gw_regex_matches(u->denied_prefix_regex, number) == MATCH)
+            goto denied;
+
+    if (u->allowed_prefix_regex == NULL && u->allowed_prefix == NULL) 
         goto no_allowed_config;
-    }
 
+    if (u->allowed_prefix != NULL) {
     allowed = octstr_split(u->allowed_prefix, octstr_imm(";"));
     for (i = 0; i < list_len(allowed); ++i) {
          listed_prefix = list_get(allowed, i);
@@ -884,6 +928,12 @@
 	         goto allowed;
          }
     }
+    }
+
+    /* note: country-prefix _must_ be included in the pattern */
+    if (u->allowed_prefix_regex != NULL) 
+        if (gw_regex_matches(u->allowed_prefix_regex, number) == MATCH)
+            goto allowed;
 
 /*
  * Here we have an intentional fall-through. It will removed when memory cleaning
@@ -912,18 +962,28 @@
 
 static int whitelisted(WAPPushUser *u, Octstr *number)
 {
-    if (u->white_list == NULL)
-        return 1;
+    int result = 1;
 
-    return numhash_find_number(u->white_list, number);
+    if (u->white_list != NULL)
+        result = numhash_find_number(u->white_list, number);
+
+    if ((result == 0) && (u->white_list_regex != NULL))
+        result = (gw_regex_matches(u->white_list_regex, number) == MATCH) ? 1 : 0;
+
+    return result;
 }
 
 static int blacklisted(WAPPushUser *u, Octstr *number)
 {
+    int result = 0;
+    
     if (u->black_list == NULL)
-        return 0;
+        result = numhash_find_number(u->black_list, number);
+
+    if ((result == 0) && (u->black_list_regex != NULL))
+        result = (gw_regex_matches(u->black_list_regex, number) == MATCH) ? 1 : 0;
 
-    return numhash_find_number(u->black_list, number);
+    return result;
 }
 
 /* 
Index: gw/urltrans.c
===================================================================
RCS file: /home/cvs/gateway/gw/urltrans.c,v
retrieving revision 1.89
diff -u -b -B -r1.89 urltrans.c
--- gw/urltrans.c	8 Dec 2003 11:25:44 -0000	1.89
+++ gw/urltrans.c	20 Jan 2004 15:01:52 -0000
@@ -71,6 +71,7 @@
 #include "urltrans.h"
 #include "gwlib/gwlib.h"
 #include "gw/sms.h"
+#include "gwlib/regex.h"
 
 
 /***********************************************************************
@@ -126,6 +127,15 @@
     int has_catchall_arg;
     int catch_all;
     Octstr *dlr_url;	/* Url to call for delivery reports */
+
+    regex_t *keyword_regex;       /* the compiled regular expression for the keyword*/
+    regex_t *accepted_smsc_regex;
+    regex_t *allowed_prefix_regex;
+    regex_t *denied_prefix_regex;
+    regex_t *allowed_receiver_prefix_regex;
+    regex_t *denied_receiver_prefix_regex;
+    regex_t *white_list_regex;
+    regex_t *black_list_regex;
 };
 
 
@@ -781,11 +792,21 @@
     return t->white_list;
 }
 
+regex_t *urltrans_white_list_regex(URLTranslation *t)
+{
+    return t->white_list_regex;
+}
+
 Numhash *urltrans_black_list(URLTranslation *t)
 {
     return t->black_list;
 }
 
+regex_t *urltrans_black_list_regex(URLTranslation *t)
+{
+    return t->black_list_regex;
+}
+
 int urltrans_assume_plain_text(URLTranslation *t) 
 {
     return t->assume_plain_text;
@@ -823,6 +844,16 @@
     Octstr *accepted_smsc, *forced_smsc, *default_smsc;
     Octstr *grpname, *sendsms_user, *sms_service;
     int is_sms_service;
+    regex_t *expr = NULL;
+    Octstr *accepted_smsc_regex;
+    Octstr *allowed_prefix_regex;
+    Octstr *denied_prefix_regex;
+    Octstr *allowed_receiver_prefix_regex;
+    Octstr *denied_receiver_prefix_regex;
+    Octstr *white_list_regex;
+    Octstr *black_list_regex;
+    Octstr *keyword_regex;
+    Octstr *os;
     
     grpname = cfg_get_group_name(grp);
     if (grpname == NULL)
@@ -868,6 +899,14 @@
     ot->denied_recv_prefix = NULL;
     ot->white_list = NULL;
     ot->black_list = NULL;
+    ot->keyword_regex = NULL;
+    ot->accepted_smsc_regex = NULL;
+    ot->allowed_prefix_regex = NULL;
+    ot->denied_prefix_regex = NULL;
+    ot->allowed_receiver_prefix_regex = NULL;
+    ot->denied_receiver_prefix_regex = NULL;
+    ot->white_list_regex = NULL;
+    ot->black_list_regex = NULL;
     
     if (is_sms_service) {
 	cfg_get_bool(&ot->catch_all, grp, octstr_imm("catch-all"));
@@ -917,6 +956,13 @@
 	octstr_convert_range(ot->keyword, 0, octstr_len(ot->keyword), 
 	    	    	     tolower);
 
+        keyword_regex = cfg_get(grp, octstr_imm("keyword-regex"));
+        if (keyword_regex != NULL) {
+            if ((ot->keyword_regex = gw_regex_comp(keyword_regex, REG_EXTENDED)) == NULL)
+                  panic(0, "Could not compile pattern '%s'", octstr_get_cstr(keyword_regex));
+            octstr_destroy(keyword_regex);
+        }
+
 	ot->name = cfg_get(grp, octstr_imm("name"));
 	if (ot->name == NULL)
 	    ot->name = octstr_duplicate(ot->keyword);
@@ -941,6 +987,12 @@
 	    ot->accepted_smsc = octstr_split(accepted_smsc, octstr_imm(";"));
 	    octstr_destroy(accepted_smsc);
 	}
+        accepted_smsc_regex = cfg_get(grp, octstr_imm("accepted-smsc-regex"));
+        if (accepted_smsc_regex != NULL) { 
+            if ( (ot->accepted_smsc_regex = gw_regex_comp(accepted_smsc_regex, REG_EXTENDED)) == NULL)
+            panic(0, "Could not compile pattern '%s'", octstr_get_cstr(accepted_smsc_regex));
+            octstr_destroy(accepted_smsc_regex);
+        }
 
 	cfg_get_bool(&ot->assume_plain_text, grp, 
 		     octstr_imm("assume-plain-text"));
@@ -951,9 +1003,22 @@
 	
 	ot->prefix = cfg_get(grp, octstr_imm("prefix"));
 	ot->suffix = cfg_get(grp, octstr_imm("suffix"));
+        ot->allowed_recv_prefix = cfg_get(grp, octstr_imm("allowed-receiver-prefix"));
+        allowed_receiver_prefix_regex = cfg_get(grp, octstr_imm("allowed-receiver-prefix-regex"));
+        if (allowed_receiver_prefix_regex != NULL) {
+            if ((ot->allowed_receiver_prefix_regex = gw_regex_comp(allowed_receiver_prefix_regex, REG_EXTENDED)) == NULL)
+            panic(0, "Could not compile pattern '%s'", octstr_get_cstr(allowed_receiver_prefix_regex));
+            octstr_destroy(allowed_receiver_prefix_regex);
+        }
 
 	ot->allowed_recv_prefix = cfg_get(grp, octstr_imm("allowed-receiver-prefix"));
     ot->denied_recv_prefix = cfg_get(grp, octstr_imm("denied-receiver-prefix"));
+        denied_receiver_prefix_regex = cfg_get(grp, octstr_imm("denied-receiver-prefix-regex"));
+        if (denied_receiver_prefix_regex != NULL) {
+            if ((ot->denied_receiver_prefix_regex = gw_regex_comp(denied_receiver_prefix_regex, REG_EXTENDED)) == NULL)
+            panic(0, "Could not compile pattern '%s'",octstr_get_cstr(denied_receiver_prefix_regex));
+            octstr_destroy(denied_receiver_prefix_regex);
+        }
 
 	ot->args = count_occurences(ot->pattern, octstr_imm("%s"));
 	ot->args += count_occurences(ot->pattern, octstr_imm("%S"));
@@ -993,22 +1058,45 @@
 	ot->deny_ip = cfg_get(grp, octstr_imm("user-deny-ip"));
 	ot->allow_ip = cfg_get(grp, octstr_imm("user-allow-ip"));
 	ot->default_sender = cfg_get(grp, octstr_imm("default-sender"));
-
     }
+    
     ot->allowed_prefix = cfg_get(grp, octstr_imm("allowed-prefix"));
+    allowed_prefix_regex = cfg_get(grp, octstr_imm("allowed-prefix-regex"));
+    if (allowed_prefix_regex != NULL) {
+        if ((ot->allowed_prefix_regex = gw_regex_comp(allowed_prefix_regex, REG_EXTENDED)) == NULL)
+            panic(0, "Could not compile pattern '%s'", octstr_get_cstr(allowed_prefix_regex));
+        octstr_destroy(allowed_prefix_regex);
+    }
     ot->denied_prefix = cfg_get(grp, octstr_imm("denied-prefix"));
-    {
-	Octstr *os;
+    denied_prefix_regex = cfg_get(grp, octstr_imm("denied-prefix-regex"));
+    if (denied_prefix_regex != NULL) {
+        if ((ot->denied_prefix_regex = gw_regex_comp(denied_prefix_regex, REG_EXTENDED)) == NULL)
+            panic(0, "Could not compile pattern '%s'", octstr_get_cstr(denied_prefix_regex));
+        octstr_destroy(denied_prefix_regex);
+    }
+    
 	os = cfg_get(grp, octstr_imm("white-list"));
 	if (os != NULL) {
 	    ot->white_list = numhash_create(octstr_get_cstr(os));
 	    octstr_destroy(os);
 	}
+    white_list_regex = cfg_get(grp, octstr_imm("white-list-regex"));
+    if (white_list_regex != NULL) {
+        if ((ot->white_list_regex = gw_regex_comp(white_list_regex, REG_EXTENDED)) == NULL)
+            panic(0, "Could not compile pattern '%s'", octstr_get_cstr(white_list_regex));
+        octstr_destroy(white_list_regex);
+    }
+
 	os = cfg_get(grp, octstr_imm("black-list"));
 	if (os != NULL) {
 	    ot->black_list = numhash_create(octstr_get_cstr(os));
 	    octstr_destroy(os);
 	}
+    black_list_regex = cfg_get(grp, octstr_imm("black-list-regex"));
+    if (black_list_regex != NULL) {
+        if ((ot->black_list_regex = gw_regex_comp(black_list_regex, REG_EXTENDED)) == NULL)
+            panic(0, "Could not compile pattern '%s'", octstr_get_cstr(black_list_regex));
+        octstr_destroy(black_list_regex);
     }
 
     if (cfg_get_integer(&ot->max_messages, grp, 
@@ -1054,7 +1142,7 @@
     
     ot = p;
     if (ot != NULL) {
-	octstr_destroy(ot->keyword);
+    if (ot->keyword != NULL) octstr_destroy(ot->keyword);
 	list_destroy(ot->aliases, octstr_destroy_item);
 	octstr_destroy(ot->dlr_url);
 	octstr_destroy(ot->pattern);
@@ -1080,98 +1168,212 @@
 	octstr_destroy(ot->denied_recv_prefix);
 	numhash_destroy(ot->white_list);
 	numhash_destroy(ot->black_list);
+    if (ot->keyword_regex != NULL) gw_regex_destroy(ot->keyword_regex);
+    if (ot->accepted_smsc_regex != NULL) gw_regex_destroy(ot->accepted_smsc_regex);
+    if (ot->allowed_prefix_regex != NULL) gw_regex_destroy(ot->allowed_prefix_regex);
+    if (ot->denied_prefix_regex != NULL) gw_regex_destroy(ot->denied_prefix_regex);
+    if (ot->allowed_receiver_prefix_regex != NULL) gw_regex_destroy(ot->allowed_receiver_prefix_regex);
+    if (ot->denied_receiver_prefix_regex != NULL) gw_regex_destroy(ot->denied_receiver_prefix_regex);
+    if (ot->white_list_regex != NULL) gw_regex_destroy(ot->white_list_regex);
+    if (ot->black_list_regex != NULL) gw_regex_destroy(ot->black_list_regex);
 	gw_free(ot);
     }
 }
 
 
 /*
- * Find the appropriate translation 
+ * checks if the number of passed words matches the service-pattern defined in the
+ * translation. returns 0 if arguments are okay, -1 otherwise.
  */
-static URLTranslation *find_translation(URLTranslationList *trans, 
-	List *words, Octstr *smsc, Octstr *sender, Octstr *receiver, int *reject)
+int check_num_args(URLTranslation *t, List *words)
 {
-    Octstr *keyword;
-    int i, n;
-    URLTranslation *t;
-    List *list;
+    const int IS_OKAY = 0;
+    const int NOT_OKAY = -1;
+    int n;
+
     
     n = list_len(words);
-    if (n == 0)
-	return NULL;
-    keyword = list_get(words, 0);
-    keyword = octstr_duplicate(keyword);
-    octstr_convert_range(keyword, 0, octstr_len(keyword), tolower);
+    /* check number of arguments */
+    if (t->catch_all)
+    return IS_OKAY;
     
-    list = dict_get(trans->dict, keyword);
-    t = NULL;
-    for (i = 0; i < list_len(list); ++i) {
-	t = list_get(list, i);
+    if (n - 1 == t->args)
+    return IS_OKAY;
+
+    if (t->has_catchall_arg && n - 1 >= t->args)
+    return IS_OKAY;
+
+    return NOT_OKAY;
+    };
+
+/*
+ * checks if a request matches the parameters of a URL-Translation, e.g. whether or not 
+ * a user is allowed to use certain services. returns 0 if allowed, -1 if not.
+ * reject will be set to 1 is a number is rejected due to white/black-lists.
+ */
+int check_allowed_translation(URLTranslation *t, 
+                  Octstr *smsc, Octstr *sender, Octstr *receiver, int *reject)
+{
+    const int IS_ALLOWED = 0;
+    const int NOT_ALLOWED = -1;
 
 	/* if smsc_id set and accepted_smsc exist, accept
 	 * translation only if smsc id is in accept string
 	 */
 	if (smsc && t->accepted_smsc) {
-	    if (!list_search(t->accepted_smsc, smsc, octstr_item_match)) {
-		t = NULL;
-		continue;
-	    }
-	}
+        if (!list_search(t->accepted_smsc, smsc, octstr_item_match))              
+            return NOT_ALLOWED;
+    };
+    if (smsc && t->accepted_smsc_regex)
+        if (gw_regex_matches( t->accepted_smsc_regex, smsc) == NO_MATCH)
+            return NOT_ALLOWED;
 
 	/* Have allowed for sender */
 	if (t->allowed_prefix && ! t->denied_prefix &&
-	   (does_prefix_match(t->allowed_prefix, sender) != 1)) {
-	    t = NULL;
-	    continue;
-	}
+        (does_prefix_match(t->allowed_prefix, sender) != 1))
+            return NOT_ALLOWED;
+
+    if (t->allowed_prefix_regex && ! t->denied_prefix_regex) 
+        if (gw_regex_matches( t->allowed_prefix_regex, sender) == NO_MATCH)
+            return NOT_ALLOWED;
 
 	/* Have denied for sender */
 	if (t->denied_prefix && ! t->allowed_prefix &&
-	   (does_prefix_match(t->denied_prefix, sender) == 1)) {
-	    t = NULL;
-	    continue;
-	}
+    (does_prefix_match(t->denied_prefix, sender) == 1))
+    return NOT_ALLOWED;
+
+    if (t->denied_prefix_regex && ! t->allowed_prefix_regex)
+        if (gw_regex_matches( t->denied_prefix_regex, sender) == NO_MATCH)
+            return NOT_ALLOWED;
 
 	/* Have allowed for receiver */
 	if (t->allowed_recv_prefix && ! t->denied_recv_prefix &&
-	   (does_prefix_match(t->allowed_recv_prefix, receiver) != 1)) {
-	    t = NULL;
-	    continue;
-	}
+    (does_prefix_match(t->allowed_recv_prefix, receiver) != 1))
+    return NOT_ALLOWED;
+
+    if (t->allowed_receiver_prefix_regex && ! t->denied_receiver_prefix_regex)
+    if (gw_regex_matches( t->allowed_receiver_prefix_regex, receiver) == NO_MATCH)
+        return NOT_ALLOWED;
 
 	/* Have denied for receiver */
 	if (t->denied_recv_prefix && ! t->allowed_recv_prefix &&
-	   (does_prefix_match(t->denied_recv_prefix, receiver) == 1)) {
-	    t = NULL;
-	    continue;
-	}
+    (does_prefix_match(t->denied_recv_prefix, receiver) == 1))
+    return NOT_ALLOWED;
+
+    if (t->denied_receiver_prefix_regex && ! t->allowed_receiver_prefix_regex)
+    if (gw_regex_matches( t->denied_receiver_prefix_regex, receiver) == NO_MATCH)
+        return NOT_ALLOWED;
 
 	if (t->white_list &&
 	    numhash_find_number(t->white_list, sender) < 1) {
-	    t = NULL; *reject = 1;
-	    continue;
+    *reject = 1;
+    return NOT_ALLOWED;
+    }
+
+    if (t->white_list_regex) 
+    if (gw_regex_matches( t->white_list_regex, sender) == NO_MATCH) {
+        *reject = 1;
+        return NOT_ALLOWED;
 	}   
+
 	if (t->black_list &&
 	    numhash_find_number(t->black_list, sender) == 1) {
-	    t = NULL; *reject = 1;
-	    continue;
+    *reject = 1;
+    return NOT_ALLOWED;
+    }
+
+    if (t->black_list_regex) 
+    if (gw_regex_matches(t->black_list_regex, sender) == MATCH) {
+        *reject = 1;
+        return NOT_ALLOWED;
 	}   
 
 	/* Have allowed and denied */
 	if (t->denied_prefix && t->allowed_prefix &&
 	   (does_prefix_match(t->allowed_prefix, sender) != 1) &&
-	   (does_prefix_match(t->denied_prefix, sender) == 1) ) {
-	    t = NULL;
+    (does_prefix_match(t->denied_prefix, sender) == 1) )
+    return NOT_ALLOWED;
+
+    if (t->denied_prefix_regex && t->allowed_prefix_regex
+    && (gw_regex_matches(t->allowed_prefix_regex, sender) == NO_MATCH)
+    && (gw_regex_matches(t->denied_prefix_regex, sender) == MATCH))
+    return NOT_ALLOWED;
+
+    return IS_ALLOWED;
+};
+
+    
+/* get_matching_translations - iterate over all translations in trans. 
+ * for each translation check whether 
+ * the translation's keyword has already been interpreted as a regexp. 
+ * if not, compile it now,
+ * otherwise retrieve compilation result from dictionary.
+ *
+ * the translations where the word matches the translation's pattern 
+ * are returned in a list
+ * 
+ */
+List* get_matching_translations(URLTranslationList *trans, Octstr *word) 
+{
+    List *list;
+    /*char *tmp_word;*/
+    int i;
+    size_t n_match = 1;
+    regmatch_t p_match[10];
+    URLTranslation *t;
+
+    gw_assert(trans != NULL && word != NULL);
+
+    list = list_create();
+    for (i = 0; i < list_len(trans->list); ++i) {
+        t = list_get(trans->list, i);
+        if (t->keyword == NULL) 
 	    continue;
+
+        /* if regex feature is used try to match */
+        if ((t->keyword_regex != NULL) && (gw_regex_exec(t->keyword_regex, word, n_match, p_match, 0) == 0))
+            list_append(list, t);
+
+        /* otherwise look for exact match */
+        if (octstr_compare(t->keyword, word) == 0) 
+            list_append(list, t);
 	}
+    return list;
+}
 
-	if (t->catch_all)
-	    break;
+/*
+ * Find the appropriate translation 
+ */
+static URLTranslation *find_translation(URLTranslationList *trans, 
+                    List *words, Octstr *smsc, Octstr *sender, Octstr *receiver, int *reject)
+{
+    Octstr *keyword;
+    int i, n;
+    URLTranslation *t;
+    List *list;
 
-	if (n - 1 == t->args)
-	    break;
-	if (t->has_catchall_arg && n - 1 >= t->args)
+    n = list_len(words);
+    if (n == 0)
+        return NULL;
+    n = 1;
+
+    keyword = list_get(words, 0);
+    keyword = octstr_duplicate(keyword);
+    octstr_convert_range(keyword, 0, octstr_len(keyword), tolower);
+
+    list = get_matching_translations(trans, keyword);
+    /*
+      list now contains all translations where the keyword of the sms matches the
+      pattern defined by the tranlsation's keyword
+    */
+    t = NULL;
+    for (i = 0; i < list_len(list); ++i) {
+        t = list_get(list, i);
+
+        if (check_allowed_translation(t, smsc, sender, receiver, reject) == 0
+            && check_num_args(t, words) == 0)
 	    break;
+
 	t = NULL;
     }
 
@@ -1180,6 +1382,7 @@
 	*reject = 0;
 
     octstr_destroy(keyword);    
+    list_destroy(list, NULL);
     return t;
 }
 
@@ -1198,58 +1401,11 @@
     t = NULL;
     for (i = 0; i < list_len(list); ++i) {
 	t = list_get(list, i);
-	if (smsc && t->accepted_smsc) {
-	    if (!list_search(t->accepted_smsc, smsc, octstr_item_match)) {
-		t = NULL;
-		continue;
-	    }
-	}
-
-	/* Have allowed sender */
-	if (t->allowed_prefix && ! t->denied_prefix &&
-		       	(does_prefix_match(t->allowed_prefix, sender) != 1)) {
-	    t = NULL;
-	    continue;
-	}
-
-	/* Have denied sender */
-	if (t->denied_prefix && ! t->allowed_prefix &&
-		       	(does_prefix_match(t->denied_prefix, sender) == 1)) {
-	    t = NULL;
-	    continue;
-	}
-
-	/* Have allowed receiver */
-	if (t->allowed_recv_prefix && ! t->denied_recv_prefix &&
-		       	(does_prefix_match(t->allowed_recv_prefix, receiver) != 1)) {
-	    t = NULL;
-	    continue;
-	}
-
-	/* Have denied receiver */
-	if (t->denied_recv_prefix && ! t->allowed_recv_prefix &&
-		       	(does_prefix_match(t->denied_recv_prefix, receiver) == 1)) {
-	    t = NULL;
-	    continue;
-	}
 
-	if (t->white_list && numhash_find_number(t->white_list, sender) < 1) {
-	    t = NULL; *reject = 1;
-	    continue;
-	}
-	if (t->black_list && numhash_find_number(t->black_list, sender) == 1) {
-	    t = NULL; *reject = 1;
-	    continue;
-										        }
+    if (check_allowed_translation(t, smsc, sender, receiver, reject) == 0)
+        break;
 
-	/* Have allowed and denied */
-	if (t->denied_prefix && t->allowed_prefix &&
-		       	(does_prefix_match(t->allowed_prefix, sender) != 1) &&
-		       	(does_prefix_match(t->denied_prefix, sender) == 1) ) {
 	    t = NULL;
-	    continue;
-	}
-	break;
     }
 
     /* Only return reject if there's only blacklisted smsc's */
Index: gw/urltrans.h
===================================================================
RCS file: /home/cvs/gateway/gw/urltrans.h,v
retrieving revision 1.29
diff -u -b -B -r1.29 urltrans.h
--- gw/urltrans.h	15 Nov 2003 13:14:23 -0000	1.29
+++ gw/urltrans.h	20 Jan 2004 15:01:52 -0000
@@ -84,6 +84,7 @@
 #include "gwlib/gwlib.h"
 #include "msg.h"
 #include "numhash.h"
+#include "gwlib/regex.h"
 
 /*
  * This is the data structure that holds the list of translations. It is
@@ -303,6 +304,8 @@
 /* Return white and black to number list */
 Numhash *urltrans_white_list(URLTranslation *t);
 Numhash *urltrans_black_list(URLTranslation *t);
+regex_t *urltrans_white_list_regex(URLTranslation *t);
+regex_t *urltrans_black_list_regex(URLTranslation *t);
 
 /* Return value of true (!0) or false (0) variables */
 int urltrans_assume_plain_text(URLTranslation *t);
Index: gw/smscconn.c
===================================================================
RCS file: /home/cvs/gateway/gw/smscconn.c,v
retrieving revision 1.42
diff -u -b -B -r1.42 smscconn.c
--- gw/smscconn.c	15 Nov 2003 13:14:23 -0000	1.42
+++ gw/smscconn.c	20 Jan 2004 15:01:52 -0000
@@ -66,6 +66,7 @@
 #include <time.h>
 
 #include "gwlib/gwlib.h"
+#include "gwlib/regex.h"
 #include "smscconn.h"
 #include "smscconn_p.h"
 #include "bb_smscconn_cb.h"
@@ -149,6 +150,11 @@
     Octstr *smsc_type;
     int ret;
     long throughput;
+    Octstr *allowed_smsc_id_regex;
+    Octstr *denied_smsc_id_regex;
+    Octstr *allowed_prefix_regex;
+    Octstr *denied_prefix_regex;
+    Octstr *preferred_prefix_regex;
 
     if (grp == NULL)
 	return NULL;
@@ -174,6 +180,11 @@
     conn->reroute = 0;
     conn->reroute_to_smsc = NULL;
     conn->reroute_by_receiver = NULL;
+    conn->allowed_smsc_id_regex = NULL;
+    conn->denied_smsc_id_regex = NULL;
+    conn->allowed_prefix_regex = NULL;
+    conn->denied_prefix_regex = NULL;
+    conn->preferred_prefix_regex = NULL;
 
 #define GET_OPTIONAL_VAL(x, n) x = cfg_get(grp, octstr_imm(n))
     
@@ -189,6 +200,27 @@
     GET_OPTIONAL_VAL(conn->log_file, "log-file");
     cfg_get_bool(&conn->alt_dcs, grp, octstr_imm("alt-dcs"));
              
+    GET_OPTIONAL_VAL(allowed_smsc_id_regex, "allowed-smsc-id-regex");
+    if (allowed_smsc_id_regex != NULL) 
+        if ((conn->allowed_smsc_id_regex = gw_regex_comp(allowed_smsc_id_regex, REG_EXTENDED)) == NULL)
+            panic(0, "Could not compile pattern '%s'", octstr_get_cstr(allowed_smsc_id_regex));
+    GET_OPTIONAL_VAL(denied_smsc_id_regex, "denied-smsc-id-regex");
+    if (denied_smsc_id_regex != NULL) 
+        if ((conn->denied_smsc_id_regex = gw_regex_comp(denied_smsc_id_regex, REG_EXTENDED)) == NULL)
+            panic(0, "Could not compile pattern '%s'", octstr_get_cstr(denied_smsc_id_regex));
+    GET_OPTIONAL_VAL(allowed_prefix_regex, "allowed-prefix-regex");
+    if (allowed_prefix_regex != NULL) 
+        if ((conn->allowed_prefix_regex = gw_regex_comp(allowed_prefix_regex, REG_EXTENDED)) == NULL)
+            panic(0, "Could not compile pattern '%s'", octstr_get_cstr(allowed_prefix_regex));
+    GET_OPTIONAL_VAL(denied_prefix_regex, "denied-prefix-regex");
+    if (denied_prefix_regex != NULL) 
+        if ((conn->denied_prefix_regex = gw_regex_comp(denied_prefix_regex, REG_EXTENDED)) == NULL)
+            panic(0, "Could not compile pattern '%s'", octstr_get_cstr(denied_prefix_regex));
+    GET_OPTIONAL_VAL(preferred_prefix_regex, "preferred-prefix-regex");
+    if (preferred_prefix_regex != NULL) 
+        if ((conn->preferred_prefix_regex = gw_regex_comp(preferred_prefix_regex, REG_EXTENDED)) == NULL)
+            panic(0, "Could not compile pattern '%s'", octstr_get_cstr(preferred_prefix_regex));
+             
     if (cfg_get_integer(&throughput, grp, octstr_imm("throughput")) == -1)
         conn->throughput = 0;   /* defaults to no throughtput limitation */
     else 
@@ -208,6 +240,9 @@
     if (conn->allowed_smsc_id && conn->denied_smsc_id)
 	warning(0, "Both 'allowed-smsc-id' and 'denied-smsc-id' set, deny-list "
 		"automatically ignored");
+    if (conn->allowed_smsc_id_regex && conn->denied_smsc_id_regex)
+        warning(0, "Both 'allowed-smsc-id_regex' and 'denied-smsc-id_regex' set, deny-regex "
+                "automatically ignored");
 
     if (cfg_get_integer(&conn->reconnect_delay, grp, 
                         octstr_imm("reconnect-delay")) == -1)
@@ -304,6 +339,12 @@
     octstr_destroy(conn->our_host);
     octstr_destroy(conn->log_file);
 
+    if (conn->denied_smsc_id_regex != NULL) gw_regex_destroy(conn->denied_smsc_id_regex);
+    if (conn->allowed_smsc_id_regex != NULL) gw_regex_destroy(conn->allowed_smsc_id_regex);
+    if (conn->preferred_prefix_regex != NULL) gw_regex_destroy(conn->preferred_prefix_regex);
+    if (conn->denied_prefix_regex != NULL) gw_regex_destroy(conn->denied_prefix_regex);
+    if (conn->allowed_prefix_regex != NULL) gw_regex_destroy(conn->allowed_prefix_regex);
+
     octstr_destroy(conn->reroute_to_smsc);
     dict_destroy(conn->reroute_by_receiver);
     
@@ -396,22 +437,50 @@
 	list_destroy(list, octstr_destroy_item);
     }
 
+    if (conn->allowed_smsc_id_regex) {
+        if (msg->sms.smsc_id == NULL)
+            return -1;
+        
+        if (gw_regex_matches(conn->allowed_smsc_id_regex, msg->sms.smsc_id) == NO_MATCH) 
+            return -1;
+    }
+    else if (conn->denied_smsc_id_regex && msg->sms.smsc_id != NULL) {
+        if (gw_regex_matches(conn->denied_smsc_id_regex, msg->sms.smsc_id) == MATCH) 
+            return -1;
+    };
+
     /* Have allowed */
     if (conn->allowed_prefix && ! conn->denied_prefix && 
        (does_prefix_match(conn->allowed_prefix, msg->sms.receiver) != 1))
 	return -1;
 
+    if (conn->allowed_prefix_regex && ! conn->denied_prefix_regex) {
+        if (gw_regex_matches(conn->allowed_prefix_regex, msg->sms.receiver) == NO_MATCH)
+            return -1;
+    }
+
     /* Have denied */
     if (conn->denied_prefix && ! conn->allowed_prefix &&
        (does_prefix_match(conn->denied_prefix, msg->sms.receiver) == 1))
 	return -1;
 
+    if (conn->denied_prefix_regex && ! conn->allowed_prefix_regex) {
+        if (gw_regex_matches(conn->denied_prefix_regex, msg->sms.receiver) == MATCH)
+            return -1;
+    }
+
     /* Have allowed and denied */
     if (conn->denied_prefix && conn->allowed_prefix &&
        (does_prefix_match(conn->allowed_prefix, msg->sms.receiver) != 1) &&
        (does_prefix_match(conn->denied_prefix, msg->sms.receiver) == 1) )
 	return -1;
 
+    if (conn->allowed_prefix_regex && conn->denied_prefix_regex) {
+        if ((gw_regex_matches(conn->allowed_prefix_regex, msg->sms.receiver) == NO_MATCH)
+            && (gw_regex_matches(conn->denied_prefix_regex, msg->sms.receiver) == MATCH))
+            return -1;
+    };
+    
     /* then see if it is preferred one */
 
     if (conn->preferred_smsc_id && msg->sms.smsc_id != NULL) {
@@ -426,6 +495,11 @@
 	if (does_prefix_match(conn->preferred_prefix, msg->sms.receiver) == 1)
 	    return 1;
 
+    if (conn->preferred_prefix_regex) {
+        if (gw_regex_matches(conn->preferred_prefix_regex, msg->sms.receiver) == MATCH)
+            return 1;
+    };
+        
     return 0;
 }
 
Index: gw/smsbox.c
===================================================================
RCS file: /home/cvs/gateway/gw/smsbox.c,v
retrieving revision 1.240
diff -u -b -B -r1.240 smsbox.c
--- gw/smsbox.c	8 Dec 2003 11:25:44 -0000	1.240
+++ gw/smsbox.c	20 Jan 2004 15:01:52 -0000
@@ -64,6 +64,7 @@
 #include <string.h>
 
 #include "gwlib/gwlib.h"
+#include "gwlib/regex.h"
 
 #include "msg.h"
 #include "sms.h"
@@ -125,6 +126,8 @@
 static int mo_recode = 0;
 static Numhash *white_list;
 static Numhash *black_list;
+static regex_t *white_list_regex = NULL;
+static regex_t *black_list_regex = NULL;
 static unsigned long max_http_retries = HTTP_MAX_RETRIES;
 static unsigned long http_queue_delay = HTTP_RETRY_DELAY;
 static Octstr *ppg_service_name = NULL;
@@ -1907,6 +1910,16 @@
         } else {
             list_append_unique(allowed, receiv, octstr_item_match);
         }
+
+        if (urltrans_white_list_regex(t) &&
+                gw_regex_matches(urltrans_white_list_regex(t), receiv) == NO_MATCH) {
+            info(0, "Number <%s> is not in white-list-regex, message discarded",
+                 octstr_get_cstr(receiv));
+            list_append_unique(denied, receiv, octstr_item_match);
+        } else {
+            list_append_unique(allowed, receiv, octstr_item_match);
+        }
+        
         if (urltrans_black_list(t) &&
             numhash_find_number(urltrans_black_list(t), receiv) == 1) {
             info(0, "Number <%s> is in black-list, message discarded",
@@ -1915,6 +1928,17 @@
         } else {
             list_append_unique(allowed, receiv, octstr_item_match);
         }
+
+        if (urltrans_black_list_regex(t) &&
+                gw_regex_matches(urltrans_black_list_regex(t), receiv) == MATCH) {
+            info(0, "Number <%s> is in black-list-regex, message discarded",
+                 octstr_get_cstr(receiv));
+            list_append_unique(denied, receiv, octstr_item_match);
+        } else {
+            list_append_unique(allowed, receiv, octstr_item_match);
+        }
+        
+
         if (white_list &&
             numhash_find_number(white_list, receiv) < 1) {
             info(0, "Number <%s> is not in global white-list, message discarded",
@@ -1923,6 +1947,16 @@
         } else {
             list_append_unique(allowed, receiv, octstr_item_match);
         }
+
+        if (white_list_regex &&
+            gw_regex_matches(white_list_regex, receiv) == NO_MATCH) {
+            info(0, "Number <%s> is not in global white-list-regex, message discarded",
+                 octstr_get_cstr(receiv));
+            list_append_unique(denied, receiv, octstr_item_match);
+        } else {
+            list_append_unique(allowed, receiv, octstr_item_match);
+        }
+
         if (black_list &&
             numhash_find_number(black_list, receiv) == 1) {
             info(0, "Number <%s> is in global black-list, message discarded",
@@ -1931,6 +1965,15 @@
         } else {
             list_append_unique(allowed, receiv, octstr_item_match);
         }
+
+        if (black_list_regex &&
+            gw_regex_matches(black_list_regex, receiv) == MATCH) {
+            info(0, "Number <%s> is in global black-list-regex, message discarded",
+                 octstr_get_cstr(receiv));
+            list_append_unique(denied, receiv, octstr_item_match);
+        } else {
+            list_append_unique(allowed, receiv, octstr_item_match);
+        }
     }
     
     /*
@@ -3155,11 +3198,25 @@
 	    white_list = numhash_create(octstr_get_cstr(os));
 	    octstr_destroy(os);
 	}
+
+	os = cfg_get(grp, octstr_imm("white-list-regex"));
+	if (os != NULL) {
+        if ((white_list_regex = gw_regex_comp(os, REG_EXTENDED)) == NULL)
+                        panic(0, "Could not compile pattern '%s'", octstr_get_cstr(os));
+        octstr_destroy(os);
+	}
+
 	os = cfg_get(grp, octstr_imm("black-list"));
 	if (os != NULL) {
 	    black_list = numhash_create(octstr_get_cstr(os));
 	    octstr_destroy(os);
 	}
+	os = cfg_get(grp, octstr_imm("black-list-regex"));
+	if (os != NULL) {
+        if ((black_list_regex = gw_regex_comp(os, REG_EXTENDED)) == NULL)
+                        panic(0, "Could not compile pattern '%s'", octstr_get_cstr(os));
+        octstr_destroy(os);
+	}
     }
 
     cfg_get_integer(&sendsms_port, grp, octstr_imm("sendsms-port"));
@@ -3367,6 +3423,8 @@
     octstr_destroy(ppg_service_name);    
     numhash_destroy(black_list);
     numhash_destroy(white_list);
+    if (white_list_regex != NULL) gw_regex_destroy(white_list_regex);
+    if (black_list_regex != NULL) gw_regex_destroy(black_list_regex);
     cfg_destroy(cfg);
 
     /* 
Index: gw/bb_smscconn.c
===================================================================
RCS file: /home/cvs/gateway/gw/bb_smscconn.c,v
retrieving revision 1.69
diff -u -b -B -r1.69 bb_smscconn.c
--- gw/bb_smscconn.c	8 Dec 2003 11:25:44 -0000	1.69
+++ gw/bb_smscconn.c	20 Jan 2004 15:01:52 -0000
@@ -113,6 +113,9 @@
 static Numhash *black_list;
 static Numhash *white_list;
 
+static regex_t *white_list_regex = NULL;
+static regex_t *black_list_regex = NULL;
+
 static long router_thread = -1;
 
 int route_incoming_to_boxc(Msg *sms);
@@ -261,6 +264,15 @@
 	msg_destroy(sms);
         return SMSCCONN_FAILED_REJECTED;
     }
+
+    if (white_list_regex && (gw_regex_matches(white_list_regex, sms->sms.sender) == NO_MATCH)) {
+        info(0, "Number <%s> is not in white-list, message discarded",
+             octstr_get_cstr(sms->sms.sender));
+        bb_alog_sms(conn, sms, "REJECTED - not white-regex-listed SMS");
+        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",
@@ -270,6 +282,14 @@
 	return SMSCCONN_FAILED_REJECTED;
     }
 
+    if (black_list_regex && (gw_regex_matches(black_list_regex, sms->sms.sender) == NO_MATCH)) {
+        info(0, "Number <%s> is not in black-list, message discarded",
+             octstr_get_cstr(sms->sms.sender));
+        bb_alog_sms(conn, sms, "REJECTED - black-regex-listed SMS");
+        msg_destroy(sms);
+        return SMSCCONN_FAILED_REJECTED;
+    };
+
     if (sms->sms.sms_type != report)
 	sms->sms.sms_type = mo;
 
@@ -415,11 +435,22 @@
         white_list = numhash_create(octstr_get_cstr(os));
 	octstr_destroy(os);
     }
+    if ((os = cfg_get(grp, octstr_imm("white-list-regex"))) != NULL) {
+        if ((white_list_regex = gw_regex_comp(os, REG_EXTENDED)) == NULL)
+            panic(0, "Could not compile pattern '%s'", octstr_get_cstr(os));
+        octstr_destroy(os);
+    }
+    
     os = cfg_get(grp, octstr_imm("black-list"));
     if (os != NULL) {
         black_list = numhash_create(octstr_get_cstr(os));
 	octstr_destroy(os);
     }
+    if ((os = cfg_get(grp, octstr_imm("black-list-regex"))) != NULL) {
+        if ((black_list_regex = gw_regex_comp(os, REG_EXTENDED)) == NULL)
+            panic(0, "Could not compile pattern '%s'", octstr_get_cstr(os));
+        octstr_destroy(os);
+    }
 
     smsc_groups = cfg_get_multi_group(cfg, octstr_imm("smsc"));
     /*
@@ -623,6 +654,8 @@
     octstr_destroy(unified_prefix);    
     numhash_destroy(white_list);
     numhash_destroy(black_list);
+    if (white_list_regex != NULL) gw_regex_destroy(white_list_regex);
+    if (black_list_regex != NULL) gw_regex_destroy(black_list_regex);
 }
 
 
Index: gw/smscconn_p.h
===================================================================
RCS file: /home/cvs/gateway/gw/smscconn_p.h,v
retrieving revision 1.41
diff -u -b -B -r1.41 smscconn_p.h
--- gw/smscconn_p.h	15 Nov 2003 13:14:23 -0000	1.41
+++ gw/smscconn_p.h	20 Jan 2004 15:01:53 -0000
@@ -142,6 +142,7 @@
 
 #include <signal.h>
 #include "gwlib/gwlib.h"
+#include "gwlib/regex.h"
 #include "smscconn.h"
 
 struct smscconn {
@@ -168,12 +169,18 @@
     Octstr *id;			/* Abstract name specified in configuration and
 				   used for logging and routing */
     Octstr *allowed_smsc_id;
+    regex_t *allowed_smsc_id_regex;
     Octstr *denied_smsc_id;
+    regex_t *denied_smsc_id_regex;
     Octstr *preferred_smsc_id;
+    regex_t *preferred_smsc_id_regex;
 
     Octstr *allowed_prefix;
+    regex_t *allowed_prefix_regex;
     Octstr *denied_prefix;
+    regex_t *denied_prefix_regex;
     Octstr *preferred_prefix;
+    regex_t *preferred_prefix_regex;
     Octstr *unified_prefix;
     
     Octstr *our_host;   /* local device IP to bind for TCP communication */
Index: doc/userguide/userguide.xml
===================================================================
RCS file: /home/cvs/gateway/doc/userguide/userguide.xml,v
retrieving revision 1.262
diff -u -b -B -r1.262 userguide.xml
--- doc/userguide/userguide.xml	18 Jan 2004 16:27:03 -0000	1.262
+++ doc/userguide/userguide.xml	20 Jan 2004 15:01:54 -0000
@@ -103,7 +103,8 @@
 
 	<abstract>
 		<title>Abstract</title> 
-		<para>This document describes how
+		<para>
+		This document describes how
 		to install and use Kannel, the Open Source WAP and SMS Gateway
 		originally developed by Wapit Ltd (now out of business) and now
 		being developed further by the open source community, namely the 
@@ -1442,6 +1443,21 @@
         queues grow very long).
      </entry></row>
 
+     <row><entry><literal>white-list-regex</literal></entry>
+        <entry>POSIX regular expression</entry>
+        <entry valign="bottom">
+        A regular expression defining the set of accepted senders. See section on regular 
+        expressions for details.
+        </entry>   
+     </row>
+     <row><entry><literal>black-list-regex</literal></entry>
+        <entry>POSIX regular expression</entry>
+        <entry valign="bottom">
+        A regular expression defining the set of rejected senders. See section on regular 
+        expressions for details.
+        </entry>   
+     </row>
+
   </tbody>
   </tgroup>
  </table>
@@ -2264,7 +2280,7 @@
         sent through this SMSC. Multiple entries are separated with 
         semicolon (';'). For example, "040;050" prevents sending of
         any SMS message with prefix of 040 or 050 through this SMSC.
-	If denied-prefix is unset, only this numbers are allowed. If
+	If denied-prefix is unset, only these numbers are allowed. If
 	set, number are allowed if present in allowed or not in 
 	denied list.
      </entry></row>
@@ -2280,7 +2296,7 @@
      <entry><literal>prefix-list</literal></entry>
      <entry valign="bottom">
         As <literal>denied-prefix</literal>, but SMS messages with receiver starting
-        with any of these prefixes is preferably sent through this
+        with any of these prefixes are preferably sent through this
         SMSC. In a case of multiple preferences, one is selected at random
         (also if there are preferences, SMSC is selected randomly)
 
@@ -2379,6 +2395,67 @@
 			to smsc-id B would look like: "A,111,222; B,333,444".
      </entry></row>
 
+     <row><entry><literal>allowed-smsc-id-regex</literal></entry>
+        <entry>POSIX regular expression</entry>
+        <entry valign="bottom">
+        SMS messages with SMSC ID equal to any of the IDs in this set of SMSC IDs
+        are always routed to this SMSC. 
+        See section on regular 
+        expressions for details.
+        </entry>   
+     </row>
+
+     <row><entry><literal>denied-smsc-id-regex</literal></entry>
+        <entry>POSIX regular expression</entry>
+        <entry valign="bottom">
+        SMS messages with SMSC ID equal to any of the IDs in this set of SMSC IDs
+        are never routed to this SMSC. 
+        See section on regular 
+        expressions for details.
+        </entry>   
+     </row>
+
+     <row><entry><literal>preferred-smsc-id-regex</literal></entry>
+        <entry>POSIX regular expression</entry>
+        <entry valign="bottom">
+        SMS messages with SMSC ID in this set of SMSC IDs are sent to this SMSC
+        as preferred.
+        See section on regular 
+        expressions for details.
+        </entry>   
+     </row>
+
+     <row><entry><literal>allowed-prefix-regex</literal></entry>
+        <entry>POSIX regular expression</entry>
+        <entry valign="bottom">
+        A set of phone number prefixes which are accepted to be
+        sent through this SMSC. 
+        See section on regular 
+        expressions for details.
+        </entry>   
+     </row>
+
+     <row><entry><literal>denied-prefix-regex</literal></entry>
+        <entry>POSIX regular expression</entry>
+        <entry valign="bottom">
+        A set of phone number prefixes which may not be
+        sent through this SMSC. 
+        See section on regular 
+        expressions for details.
+        </entry>   
+     </row>
+
+     <row><entry><literal>preferred-prefix-regex</literal></entry>
+        <entry>POSIX regular expression</entry>
+        <entry valign="bottom">
+        As <literal>denied-prefix-regex</literal>, but SMS messages with receiver matching 
+        any of these prefixes are preferably sent through this
+        SMSC. In a case of multiple preferences, one is selected at random.
+        See section on regular 
+        expressions for details.
+        </entry>   
+     </row>
+
   </tbody>
   </tgroup>
  </table>
@@ -4423,6 +4500,26 @@
         non-zero value.
      </entry></row>
 
+     <row><entry><literal>white-list-regex</literal></entry>
+        <entry>POSIX regular expression</entry>
+        <entry valign="bottom">
+        Defines the set of accepted destinations of SMS messages. If a 
+	destination of an SMS message is not in this set, any message 
+	received from the HTTP interface is rejected. 
+        See section on regular 
+        expressions for details.
+        </entry>   
+     </row>
+     <row><entry><literal>black-list-regex</literal></entry>
+        <entry>POSIX regular expression</entry>
+        <entry valign="bottom">
+        As white-list-regex, but SMS messages to numbers within in this set are
+        automatically discarded.
+        See section on regular 
+        expressions for details.
+        </entry>   
+     </row>
+
   </tbody>
   </tgroup>
  </table>
@@ -4612,6 +4709,15 @@
 		  cased versions of your keyword.
      </entry></row>
 
+   <row><entry><literal>keyword-regex</literal></entry>
+     <entry>POSIX regular expression</entry>
+     <entry valign="bottom">
+     This field may be used to enable service-selection based on a regular expression.
+     If this field is defined for a service, then the selection will rely on the regex only, never taking
+     the literal <literal>keyword</literal> into account.
+     See section on regular expressions for details.
+   </entry></row>
+
    <row><entry><literal>aliases</literal></entry>
      <entry>word-list</entry>
      <entry valign="bottom">
@@ -4878,6 +4984,83 @@
         automatically discarded
      </entry></row>
 
+     <row><entry><literal>accepted-smsc-regex</literal></entry>
+        <entry>POSIX regular expression</entry>
+        <entry valign="bottom">
+        Accept only SMS messages arriving from SMSCs with a matching ID.
+        <footnote><para>Even if this service is denied,
+	Kannel still searches for other service which accepts the message,
+	or default service.</para></footnote>
+        See section on regular 
+        expressions for details.
+        </entry>   
+     </row>
+
+     <row><entry><literal>allowed-prefix-regex</literal></entry>
+        <entry>POSIX regular expression</entry>
+        <entry valign="bottom">
+        A set of phone number prefixes of sender-numbers accepted by this service.
+        <footnote><para>Like in accepted-smsc-regex, 
+	Kannel still searches for another service which accepts the message.
+	This way there could be several services with the same keyword and
+	different results.</para></footnote>
+        See section on regular 
+        expressions for details.
+        </entry>   
+     </row>
+
+     <row><entry><literal>denied-prefix-regex</literal></entry>
+        <entry>POSIX regular expression</entry>
+        <entry valign="bottom">
+        A set of phone number prefixes of sender-numbers which may
+        not use this service.
+        See section on regular 
+        expressions for details.
+        </entry>   
+     </row>
+
+     <row><entry><literal>allowed-receiver-prefix-regex</literal></entry>
+        <entry>POSIX regular expression</entry>
+        <entry valign="bottom">
+        A set of phone number prefixes of receiver-numbers which 
+        may receive data sent by this service. This can be used to
+        allow only inbound SMS to certain shortcut numbers to be allowed
+	to this service.
+        See section on regular 
+        expressions for details.
+        </entry>   
+     </row>
+
+     <row><entry><literal>denied-receiver-prefix-regex</literal></entry>
+        <entry>POSIX regular expression</entry>
+        <entry valign="bottom">
+        A set of phone number prefixes of receiver-numbers which may not
+        receive data sent by this service.
+        See section on regular 
+        expressions for details.
+        </entry>   
+     </row>
+
+     <row><entry><literal>white-list-regex</literal></entry>
+        <entry>POSIX regular expression</entry>
+        <entry valign="bottom">
+        Defines a set of accepted senders of SMS messages. If a 
+	sender of an SMS message is not in this list, the message is rejected.
+        See section on regular 
+        expressions for details.
+        </entry>   
+     </row>
+
+     <row><entry><literal>black-list-regex</literal></entry>
+        <entry>POSIX regular expression</entry>
+        <entry valign="bottom">
+        As white-list-regex, but SMS messages from these numbers are
+        automatically discarded.
+        See section on regular 
+        expressions for details.
+        </entry>   
+     </row>
+
   </tbody>
   </tgroup>
  </table>
@@ -5486,6 +5669,43 @@
 	parameter is present.
      </entry></row>
 
+     <row><entry><literal>allowed-prefix-regex</literal></entry>
+        <entry>POSIX regular expression</entry>
+        <entry valign="bottom">
+        A set of phone numbers which are accepted to be
+        sent using this username. 
+        See section on regular expressions for details.
+        </entry>
+     </row>
+
+     <row><entry><literal>denied-prefix-regex</literal></entry>
+        <entry>POSIX regular expression</entry>
+        <entry valign="bottom">
+        A set of phone numbers which may not
+        send using this username. 
+        See section on regular expressions for details.
+        </entry>
+     </row>
+
+     <row><entry><literal>white-list-regex</literal></entry>
+        <entry>POSIX regular expression</entry>
+        <entry valign="bottom">
+        Defines a set of accepted destinations of SMS messages. If a 
+	destination of an SMS message is not in this list, any message 
+	received from the HTTP interface is rejected. 
+        See section on regular expressions for details.
+        </entry>
+     </row>
+
+     <row><entry><literal>black-list-regex</literal></entry>
+        <entry>POSIX regular expression</entry>
+        <entry valign="bottom">
+        As <literal>white-list-regex</literal>, but SMS messages originating from 
+        a number matching the pattern are discarded.
+        See section on regular expressions for details.
+        </entry>
+     </row>
+
   </tbody>
   </tgroup>
  </table>
@@ -6906,6 +7126,44 @@
              <entry valign="bottom">
              Allow only routing to a defined SMSC ID for this specific push user.
      	     </entry></row>
+
+     <row><entry><literal>white-list-regex</literal></entry>
+        <entry>POSIX regular expression</entry>
+        <entry valign="bottom">
+             Defines the set of phone-numbers accepting pushes from this 
+             pi.
+        See section on regular 
+        expressions for details.
+        </entry>   
+     </row>
+
+     <row><entry><literal>black-list-regex</literal></entry>
+        <entry>POSIX regular expression</entry>
+        <entry valign="bottom">
+             Defines the set of phone-numbers rejecting pushes from this 
+             pi.
+        See section on regular 
+        expressions for details.
+        </entry>   
+     </row>
+
+     <row><entry><literal>allowed-prefix-regex</literal></entry>
+        <entry>POSIX regular expression</entry>
+        <entry valign="bottom">
+             Set of phone number prefixes allowed in pushes coming from this pi. 
+        See section on regular 
+        expressions for details.
+        </entry>   
+     </row>
+     <row><entry><literal>denied-list-regex</literal></entry>
+        <entry>POSIX regular expression</entry>
+        <entry valign="bottom">
+             Set of phone number prefixes denied in pushes coming from this pi. 
+        See section on regular 
+        expressions for details.
+        </entry>   
+     </row>
+
              </tbody>
              </tgroup>
       </table>
@@ -8049,6 +8307,209 @@
 
 </appendix>
 
+<appendix>
+    <title>Regular Expressions</title>
+    <para>Note: this is not intended to be an introduction to regular expressions (short: regex) but a 
+    description of their application within Kannel. For general information regarding regexes
+    please refer to <xref linkend="Biblio-regexp"/>.
+    </para>
+    <sect1>
+        <title>Syntax and semantics of the regex configuration parameter</title>
+        <para>This section describes the regex-configuration parameters and their effects in combination with the
+        respective non-regex-parameter, e.g. <literal>white-list</literal> and <literal>white-list-regex</literal>.</para>
+
+        <sect2>
+                <title>How-to setup the regex-parameters</title>
+                <para>examples, short syntax, what happens on errors
+                Regex-parameters are configured just as every other parameter is configured. Regular expressions are
+                supported as defined by POSIX Extended Regular Expressions. Suppose a configuration where only SMS messages
+                originating from a sender using a number with a prefix of "040", "050", "070" or "090" are accepted.
+                Without regexes the configuration would read
+                <programlisting>
+                allowed-prefix="040;050;070;090"
+                </programlisting>
+                Using regular expressions yields a more concise configuration
+                <programlisting>
+                allowed-prefix-regex=^0[4579]0
+                </programlisting>
+                The following table gives an overview over some regex-operators and their meaning, the POSIX Regular
+                Expressions manual page (regex(7)). Once again, the extended regex-syntax is used and the table is 
+                just meant as a means to give a quickstart to regular expressions, the next section features some more
+                complex examples.
+                <informaltable>
+                        <tgroup cols="2">
+                                <thead>
+                                        <row>
+                                        <entry>Operator</entry>
+                                        <entry>Meaning</entry>
+                                        </row>
+                                </thead>    
+                                <tbody>
+                                        <row>
+                                        <entry><literal>|</literal></entry>
+                                        <entry>or, for example "dog|hog" matches dog or hog.</entry>
+                                        </row>
+                                        <row>
+                                        <entry><literal>{number,number}</literal></entry>
+                                        <entry>repetition, for example "a{2,5}" matches - among others - 
+                                        "aa", "aaa" and "baaaaad"</entry>
+                                        </row>
+                                        <row>
+                                        <entry><literal>*</literal></entry>
+                                        <entry>shorthand for {0,}</entry>
+                                        </row>
+                                        <row>
+                                        <entry><literal>?</literal></entry>
+                                        <entry>shorthand for {0,1}</entry>
+                                        </row>
+                                        <row>
+                                        <entry><literal>+</literal></entry>
+                                        <entry>shorthand for {1,}</entry>
+                                        </row>
+                                        <row>
+                                        <entry><literal>[]</literal></entry>
+                                        <entry>bracket expression, defines a class of possible single character matches.
+                                        For example "[hb]og" matches "hog" and "bog". If the expression starts with
+                                        <literal>^</literal> then the class is negated, e.g. "[^hb]og" does not match
+                                        "hog" and "bog" but matches for example "dog".</entry>
+                                        </row>
+                                        <row>
+                                        <entry><literal>()</literal></entry>
+                                        <entry>groups patterns, e.g. "[hb]o(g|ld)" matches "hog", "hold", "bog", "bold"
+                                        </entry>
+                                        </row>
+                                        <row>
+                                        <entry><literal>[:class:]</literal></entry>
+                                        <entry>A character class such as digit, space etc. See wctype(3) for details.
+                                        </entry>
+                                        </row>
+                                        <row>
+                                        <entry><literal>^</literal></entry>
+                                        <entry>Start of line anchor.</entry>
+                                        </row>
+                                        <row>
+                                        <entry><literal>$</literal></entry>
+                                        <entry>End of line anchor.</entry>
+                                        </row>
+                                </tbody>
+                        </tgroup>
+                </informaltable>
+
+                The advantages of regular expressions are at hand
+		<itemizedlist>
+		<listitem><para>Regexes are easier to understand, if one is fluent in POSIX Regular Expressions. 
+                                Although simple expressions as shown above should be clear to everyone who has
+                                ever used a standard UN*X shell.
+                          </para></listitem>
+                <listitem>
+                        <para>Regexes are easier to maintain. Suppose the example above needed to cope with 
+                        dozens of different prefixes each with subtle differences, in such cases using a - carefully
+                        constructed - regular expression could help to keep things in apple pie order. 
+                        Furthermore regexes help reducing redundancy within the configuration.
+                        </para>
+                </listitem>
+                <listitem>
+                        <para>Regexes more flexible than standard parameters.
+                        </para>
+                </listitem>
+                </itemizedlist>
+                Nevertheless, it must be mentioned that - in addition to the overhead involved - complexity is an issue, 
+                too. Although the syntactic correctness of each used regular expression is ensured (see below)
+                    the semantic correctness cannot be automatically proofed.
+                </para>
+                <para>
+                Expressions that are not compileable, which means they are not valid POSIX regexes, force  Kannel
+                to panic with a message like (note the missing "]")
+	<screen><computeroutput>
+                ERROR: gwlib/regex.c:106: gw_regex_comp_real: regex compilation `[hbo(g|ld)' failed: Invalid regular expression (Called from gw/urltrans.c:987:create_onetrans.)
+                PANIC: Could not compile pattern '[hbo(g|ld)'
+	</computeroutput></screen>
+                As shown the erroneous pattern is reported in the error message. 
+                </para>
+        </sect2>
+        
+        <sect2>
+                <title>Regex and non-regex-parameters</title>
+                <para>
+                Using the regex and non-regex version of a parameter at the same time should be done with caution.
+                Both are combined in a boolean-or sense, for example
+                <programlisting>
+                white-list=01234
+                white-list-regex=^5(23)?$
+                </programlisting>
+                implies that a number is accepted either if it is "01234", "5" or "523" - note the use of anchors!
+                The same goes for all the other parameters, thus both mechanisms can be used without problems in parallel,
+                but care should be taken that the implications are understood and wanted.
+                </para>
+        </sect2>
+        
+        <sect2>
+                <title>Performance issues</title>
+                <para>While there is some overhead involved, the actual performance degradation is neglectible.
+                At startup - e.g. when the configuration files are parsed - the regular expressions are pre-compiled
+                and stored in the pre-compiled fassion, thus future comparisons involve executing the expression on 
+                some string only. To be on the sure side, before using regexes extensivly some benchmarking should be
+                performed, to verify that the loss of performance is acceptable.
+                </para>
+        </sect2>
+    </sect1>
+    <sect1>
+        <title>Examples</title>
+        <para>This section discusses some simple scenarios and potential solutions based on regexes.
+        The examples are not meant to be comprehensive but rather informative.</para>
+        <sect2>
+                <title>Example 1: core-configuration</title>
+                <para>The bearerbox must only accept SMS messages from three costumers. The first costumer uses numbers
+                that always start with "0824" the second one uses numbers that start with either "0123" and end in 
+                "314" or start with "0882" and end in "666". The third costumer uses numbers starting with "0167"
+                and ending in a number between "30" and "57".</para>
+                <para>
+                Important in this and in the following examples is the use of anchors, otherwise a "string contains"
+                semantic instead of a "string is equal" semantic would be used.
+                </para>
+                <programlisting>
+                group=core
+                ...
+                white-list-regex=^((0824[0-9]+)|(0123[0-9]+314)|(0882[0-9]+666)|(0167[0-9]+([34][0-9]|5[0-7])))$
+                ...
+                </programlisting>
+        </sect2>
+        <sect2>
+                <title>Example 3: smsc-configuration</title>
+                <para>Only SMS messages originating from certain smscs (<literal>smsc-id</literal> is either 
+                        "foo", "bar" or "blah")
+                are preferably forwarded to this smsc. 
+                Furthermore all smscs with an id containing "vodafone" must never
+                be forwarded to the smsc. Not the missing anchors around "vodafone".</para>
+                <programlisting>
+                group=smsc
+                ...
+                preferred-smsc-id-regex=^(foo|bar|blah)$
+                denied-prefix-regex=vodafone
+                ...
+                </programlisting>
+        </sect2>
+        <sect2>
+                <title>Example 4: sms-service-configuration</title>
+                <para>Please note that there are a mandatory <literal>keyword</literal> and an optional
+                <literal>keyword-regex</literal> fields.
+                That means that service selection can be simplified as in the following example.
+                Suppose that some Web-content should be delivered to the mobile. Different costumers use the 
+                same service but they rely on different keywords. Whenever a sms-service is requested, Kannel
+                first checks whether a regex has been defined, if not a literal match based on <literal>keyword</literal>
+                is performed. If a regex is configured then the literal match is never tried.
+                </para>
+                <programlisting>
+                group=sms-service
+                ...
+                keyword=web_service
+                keyword-regex=^(data|www|text|net)$
+                get-url=http://someserver.net/getContent.jsp
+                ...
+                </programlisting>
+        </sect2>
+    </sect1>
+</appendix> 
 
 <appendix>
 <title>Log files</title>
@@ -8223,6 +8683,14 @@
                 <copyright><year>?</year></copyright>
         </biblioentry>
 
+        <biblioentry id="Biblio-regexp">
+                <title>regex(7), GNU regex manual</title>
+                <!-- <issn><ulink url="http://techpubs.sgi.com/library/tpl/cgi-bin/getdoc.cgi?coll=linux&db=man&fname=/usr/share/catman/man7/regex.7.html&srch=regex">http://techpubs.sgi.com/library/tpl/cgi-bin/getdoc.cgi?coll=linux&db=man&fname=/usr/share/catman/man7/regex.7.html&srch=regex</ulink></issn>-->
+                <subtitle></subtitle>
+                <orgname>GNU</orgname>
+                <copyright><year>1998</year></copyright>
+        </biblioentry>
+        
 </bibliography>
 
 </book>
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.