Common subdirectories: ../jabberd-2.0s6.orig/sm/.deps and sm/.deps
Only in ../jabberd-2.0s6.orig/sm/: .libs
diff -u ../jabberd-2.0s6.orig/sm/mod_offline.c sm/mod_offline.c
--- ../jabberd-2.0s6.orig/sm/mod_offline.c	Thu Dec 30 14:59:02 2004
+++ sm/mod_offline.c	Thu Dec 30 16:39:50 2004
@@ -27,6 +27,12 @@
   * $Revision: 1.3 $
   */
 
+typedef struct _mod_offline_st {
+    int dropmessages;
+    int dropsubscriptions;
+    int userquota;
+} *mod_offline_t;
+
 static mod_ret_t _offline_in_sess(mod_instance_t mi, sess_t sess, pkt_t pkt) {
     st_ret_t ret;
     os_t os;
@@ -95,10 +101,13 @@
 }
 
 static mod_ret_t _offline_pkt_user(mod_instance_t mi, user_t user, pkt_t pkt) {
+    mod_offline_t offline = (mod_offline_t) mi->mod->private;
     int ns, elem, attr;
     os_t os;
     os_object_t o;
     pkt_t event;
+    st_ret_t ret;
+    int queuesize;
 
     /* send messages and s10ns to the top session */
     if(user->top != NULL && (pkt->type & pkt_MESSAGE || pkt->type & pkt_S10N)) {
@@ -106,8 +115,20 @@
         return mod_HANDLED;
     }
 
+    /* if user quotas are enabled, count the number of offline messages this user has in the queue */
+    if(offline->userquota > 0) {
+        ret = storage_count(user->sm->st, "queue", jid_user(user->jid), NULL, &queuesize);
+
+        log_debug(ZONE, "storage_count ret is %i queue size is %i", ret, queuesize);
+
+        /* if the user's quota is exceeded, return an error */
+        if (ret == st_SUCCESS && (pkt->type & pkt_MESSAGE) && queuesize >= offline->userquota)
+           return -stanza_err_SERVICE_UNAVAILABLE;
+    }
+
     /* save messages and s10ns for later */
-    if(pkt->type & pkt_MESSAGE || pkt->type & pkt_S10N) {
+    if((pkt->type & pkt_MESSAGE && !offline->dropmessages) ||
+       (pkt->type & pkt_S10N && !offline->dropsubscriptions)) {
         log_debug(ZONE, "saving message for later");
 
         pkt_delay(pkt, time(NULL), user->sm->id);
@@ -222,14 +243,38 @@
     storage_delete(mi->sm->st, "queue", jid_user(jid), NULL);
 }
 
+static void _offline_free(module_t mod) {
+    mod_offline_t offline = (mod_offline_t) mod->private;
+
+    free(offline);
+}
+
 int offline_init(mod_instance_t mi, char *arg) {
     module_t mod = mi->mod;
+    char *configval;
+    mod_offline_t offline;
 
     if(mod->init) return 0;
 
+    offline = (mod_offline_t) malloc(sizeof(struct _mod_offline_st));
+    memset(offline, 0, sizeof(struct _mod_offline_st));
+
+    configval = config_get_one(mod->mm->sm->config, "offline.dropmessages", 0);
+    if (configval != NULL)
+        offline->dropmessages = 1;
+
+    configval = config_get_one(mod->mm->sm->config, "offline.dropsubscriptions", 0);
+    if (configval != NULL)
+        offline->dropsubscriptions = 1;
+
+    offline->userquota = j_atoi(config_get_one(mod->mm->sm->config, "offline.userquota", 0), 0);
+
+    mod->private = offline;
+
     mod->in_sess = _offline_in_sess;
     mod->pkt_user = _offline_pkt_user;
     mod->user_delete = _offline_user_delete;
+    mod->free = _offline_free;
 
     return 0;
 }
Only in ../jabberd-2.0s6.orig/sm/: sm
diff -u ../jabberd-2.0s6.orig/sm/sm.h sm/sm.h
--- ../jabberd-2.0s6.orig/sm/sm.h	Thu Dec 30 14:59:02 2004
+++ sm/sm.h	Thu Dec 30 15:06:59 2004
@@ -584,6 +584,8 @@
     st_ret_t    (*put)(st_driver_t drv, const char *type, const char *owner, os_t os);
     /** get handler */
     st_ret_t    (*get)(st_driver_t drv, const char *type, const char *owner, const char *filter, os_t *os);
+    /** count handler */
+    st_ret_t    (*count)(st_driver_t drv, const char *type, const char *owner, const char *filter, int *count);
     /** delete handler */
     st_ret_t    (*delete)(st_driver_t drv, const char *type, const char *owner, const char *filter);
     /** replace handler */
@@ -605,6 +607,8 @@
 st_ret_t        storage_put(storage_t st, const char *type, const char *owner, os_t os);
 /** get objects matching this filter */
 st_ret_t        storage_get(storage_t st, const char *type, const char *owner, const char *filter, os_t *os);
+/** count objects matching this filter */
+st_ret_t        storage_count(storage_t st, const char *type, const char *owner, const char *filter, int *count);
 /** delete objects matching this filter */
 st_ret_t        storage_delete(storage_t st, const char *type, const char *owner, const char *filter);
 /** replace objects matching this filter with objects in this set (atomic delete + get) */
Only in sm: smlog
diff -u ../jabberd-2.0s6.orig/sm/storage.c sm/storage.c
--- ../jabberd-2.0s6.orig/sm/storage.c	Thu Dec 30 14:59:02 2004
+++ sm/storage.c	Thu Dec 30 16:42:34 2004
@@ -270,6 +270,32 @@
     return (drv->get)(drv, type, owner, filter, os);
 }
 
+st_ret_t storage_count(storage_t st, const char *type, const char *owner, const char *filter, int *count) {
+    st_driver_t drv;
+    st_ret_t ret;
+
+    log_debug(ZONE, "storage_count: type=%s owner=%s filter=%s", type, owner, filter);
+
+    /* find the handler for this type */
+    drv = xhash_get(st->types, type);
+    if(drv == NULL) {
+        /* never seen it before, so it goes to the default driver */
+        drv = st->default_drv;
+        if(drv == NULL) {
+            log_debug(ZONE, "no driver associated with type, and no default driver");
+            return st_NOTIMPL;
+        }
+
+        /* register the type */
+        ret = storage_add_type(st, drv->name, type);
+        if(ret != st_SUCCESS)
+            return ret;
+    }
+
+    return (drv->count != NULL) ? (drv->count)(drv, type, owner, filter, count) : st_NOTIMPL;
+}
+
+
 st_ret_t storage_delete(storage_t st, const char *type, const char *owner, const char *filter) {
     st_driver_t drv;
     st_ret_t ret;
diff -u ../jabberd-2.0s6.orig/sm/storage_mysql.c sm/storage_mysql.c
--- ../jabberd-2.0s6.orig/sm/storage_mysql.c	Thu Dec 30 14:59:02 2004
+++ sm/storage_mysql.c	Thu Dec 30 16:44:37 2004
@@ -438,6 +438,74 @@
     return st_SUCCESS;
 }
 
+static st_ret_t _st_mysql_count(st_driver_t drv, const char *type, const char *owner, const char *filter, int *count) {
+    drvdata_t data = (drvdata_t) drv->private;
+    char *cond, *buf = NULL;
+    int buflen = 0;
+    MYSQL_RES *res;
+    int ntuples, nfields;
+    MYSQL_ROW tuple;
+    char tbuf[128];
+
+    if(mysql_ping(data->conn) != 0) {
+        log_write(drv->st->sm->log, LOG_ERR, "mysql: connection to database lost");
+        return st_FAILED;
+    }
+
+    if(data->prefix != NULL) {
+        snprintf(tbuf, sizeof(tbuf), "%s%s", data->prefix, type);
+        type = tbuf;
+    }
+
+    cond = _st_mysql_convert_filter(drv, owner, filter);
+    log_debug(ZONE, "generated filter: %s", cond);
+
+    MYSQL_SAFE(buf, strlen(type) + strlen(cond) + 31, buflen);
+    sprintf(buf, "SELECT COUNT(*) FROM `%s` WHERE %s", type, cond);
+    free(cond);
+
+    log_debug(ZONE, "prepared sql: %s", buf);
+
+    if(mysql_query(data->conn, buf) != 0) {
+        log_write(drv->st->sm->log, LOG_ERR, "mysql: sql select failed: %s", mysql_error(data->conn));
+        free(buf);
+        return st_FAILED;
+    }
+    free(buf);
+
+    res = mysql_store_result(data->conn);
+    if(res == NULL) {
+        log_write(drv->st->sm->log, LOG_ERR, "mysql: sql result retrieval failed: %s", mysql_error(data->conn));
+        return st_FAILED;
+    }
+
+    ntuples = mysql_num_rows(res);
+    if(ntuples == 0) {
+        mysql_free_result(res);
+        return st_NOTFOUND;
+    }
+
+    log_debug(ZONE, "%d tuples returned", ntuples);
+
+    nfields = mysql_num_fields(res);
+
+    if(nfields == 0) {
+        log_debug(ZONE, "weird, tuples were returned but no fields *shrug*");
+        mysql_free_result(res);
+        return st_NOTFOUND;
+    }
+
+    if((tuple = mysql_fetch_row(res)) == NULL)
+        return st_NOTFOUND;
+
+    if (count!=NULL)
+        *count = atoi(tuple[0]);
+
+    mysql_free_result(res);
+
+    return st_SUCCESS;
+}
+
 static st_ret_t _st_mysql_delete(st_driver_t drv, const char *type, const char *owner, const char *filter) {
     drvdata_t data = (drvdata_t) drv->private;
     char *cond, *buf = NULL;
@@ -571,6 +639,7 @@
 
     drv->add_type = _st_mysql_add_type;
     drv->put = _st_mysql_put;
+    drv->count = _st_mysql_count;
     drv->get = _st_mysql_get;
     drv->delete = _st_mysql_delete;
     drv->replace = _st_mysql_replace;
