diff -Naur config.h.in config.h.in
--- config.h.in	2004-06-24 18:55:19.000000000 -0400
+++ config.h.in	2005-06-30 09:54:27.000000000 -0400
@@ -420,6 +420,9 @@
 /* Define to 1 if you want to use Berkeley DB for auth/reg/storage. */
 #undef STORAGE_DB
 
+/* Define to 1 if you want to use LDAP for vCard storage. */
+#undef STORAGE_LDAPVCARD
+
 /* Define to 1 if you want to use the filesystem for storage. */
 #undef STORAGE_FS
 
diff -Naur configure configure
--- configure	2004-06-24 18:53:57.000000000 -0400
+++ configure	2005-06-30 10:18:46.000000000 -0400
@@ -21076,6 +21076,7 @@
 
 cat >>confdefs.h <<\_ACEOF
 #define STORAGE_LDAP 1
+#define STORAGE_LDAPVCARD 1
 _ACEOF
 
     fi
diff -Naur configure.in configure.in
--- configure.in	2004-06-24 18:50:26.000000000 -0400
+++ configure.in	2005-06-30 09:54:27.000000000 -0400
@@ -260,6 +260,7 @@
         AC_MSG_ERROR([OpenLDAP client libraries >= 2.1.0 not found])
     else
         AC_DEFINE(STORAGE_LDAP,1,[Define to 1 if you want to use OpenLDAP for auth/reg.])
+	AC_DEFINE(STORAGE_LDAPVCARD,1,[Define to 1 if you want to use OpenLDAP for storage.])
     fi
 fi
 
diff -Naur etc/sm.xml.dist.in etc/sm.xml.dist.in
--- etc/sm.xml.dist.in	2004-04-01 23:32:31.000000000 -0500
+++ etc/sm.xml.dist.in	2005-06-30 10:09:40.000000000 -0400
@@ -74,6 +74,46 @@
     <!--
     <driver type='vcard'>pgsql</driver>
     -->
+    <!--
+    <driver type='vcard'>ldapvcard</driver>
+    -->
+    <!-- The published-roster type only works with the ldapvcard storage
+         driver, so if you have a <publish/> item in your user template,
+         you will need to uncomment this.
+    -->
+    <!--
+    <driver type='published-roster'>ldapvcard</driver>
+    -->
+
+    <ldapvcard>
+      <!-- LDAP server host and port (default: 389) -->
+      <uri>ldap://localhost/ ldaps://ldap.example.com/</uri>
+
+      <!-- DN to bind as for searches. If unspecified, the searches
+           will be done anonymously. -->
+      <!--
+      <binddn>cn=Directory Manager</binddn>
+      <bindpw>secret</bindpw>
+      -->
+
+      <!-- LDAP attribute that holds the user ID (default: uid) -->
+      <uidattr>uid</uidattr>
+      <!-- LDAP object class to filter by -->
+      <objectclass>inetOrgPerson</objectclass>
+      <!-- LDAP attribute that holds the user group (default:
+           jabberPublishedGroup) -->
+      <groupattr>departmentNumber</groupattr>
+      <!-- LDAP attribute that determines whether the item should be published
+           (default: jabberPublishedItem). If this isn't set, all user items 
+           will be shown. -->
+      <publishedattr>jabberPublishedItem</publishedattr>
+
+      <!-- base DN of the tree. You should specify a DN for each
+           authentication realm declared in the <local/> section above,
+           by using the realm attribute. -->
+      <basedn>o=Example Corp.</basedn>
+    </ldapvcard>
+
 
     <!-- MySQL driver configuration -->
     <mysql>
@@ -274,6 +314,7 @@
     <chain id='user-load'>
       <module>active</module>           <!-- get active status -->
       <module>roster</module>           <!-- load the roster and trust list -->
+      <module>roster-publish</module>   <!-- load the published roster -->
       <module>privacy</module>          <!-- load privacy lists -->
       <module>disco-publish</module>    <!-- load published information -->
       <module>vacation</module>         <!-- load vacation settings -->
@@ -373,6 +414,50 @@
     <!-- Templates. If defined, the contents of these files will be
          stored in the users data store when they are created. -->
     <template>
+      <!-- Uncomment <publish> if you wish to force publication of
+           roster template from ldap on each user login -->
+      <!--
+      <publish>
+      -->
+        <!-- If <check-remove-domain> given, then published contact checked
+               against sm user database and if user is unknown to sm, contact
+               will be deleted from user's roster (if it is in roster). -->
+        <!--
+        <check-remove-domain>jabber.example.com</check-remove-domain>
+        -->
+        <!-- If <fix-subscriptions/> is not commented, set subscriptions of
+             user's contacts to subscriptions of corresponding published
+             contacts. As for now, "both" -->
+        <!--
+        <fix-subscriptions/>
+        -->
+        <!-- If <force-groups> is commented out, published roster's contact
+             added to user's roster only when user does not have this contact.
+             If <force-groups> is not commented out, then these checks performed
+             against roster item when publishing roster item that already in
+             user's roster:
+               If user already has added his roster's contact to group of
+               published contact, no changes are made with this group (note
+               that contact may be in more than one group).
+               If <prefix> given, then prefix of each group of user's compared
+               whith given prefix, and if it matches, user's contact removed from
+               matched group (see below).
+               Same for <suffix>.
+               After that, user's contact added to a group of published roster's
+               contact.
+             In other words, all groups of updated contact, that match prefix
+             or suffix, replaced with group of published contact. -->
+        <!--
+        <force-groups>
+          <prefix>MyOrg.</prefix>
+          <suffix>(MyOrg)</suffix>
+        </force-groups>
+        -->
+      <!--
+      </publish>
+      -->
+      <!-- If you defined publish, you should comment out <roster> -->
+
       <!--
       <roster>@sysconfdir@/templates/roster.xml</roster>
       -->
diff -Naur sm/Makefile.am sm/Makefile.am
--- sm/Makefile.am	2004-04-07 08:14:47.000000000 -0400
+++ sm/Makefile.am	2005-06-30 09:54:27.000000000 -0400
@@ -16,6 +16,7 @@
              user.c \
              storage.c \
              storage_db.c \
+	     storage_ldapvcard.c \
              storage_fs.c \
              storage_mysql.c \
              storage_pgsql.c \
@@ -30,6 +31,7 @@
              mod_presence.c \
              mod_privacy.c \
              mod_roster.c \
+	     mod_roster_publish.c \
              mod_session.c \
              mod_vacation.c \
              mod_validate.c \
diff -Naur sm/Makefile.in sm/Makefile.in
--- sm/Makefile.in	2004-06-24 18:56:14.000000000 -0400
+++ sm/Makefile.in	2005-06-30 09:54:27.000000000 -0400
@@ -109,6 +109,7 @@
              user.c \
              storage.c \
              storage_db.c \
+	     storage_ldapvcard.c \
              storage_fs.c \
              storage_mysql.c \
              storage_pgsql.c \
@@ -123,6 +124,7 @@
              mod_presence.c \
              mod_privacy.c \
              mod_roster.c \
+	     mod_roster_publish.c \
              mod_session.c \
              mod_vacation.c \
              mod_validate.c \
@@ -151,13 +153,13 @@
 am_sm_OBJECTS = aci.$(OBJEXT) dispatch.$(OBJEXT) feature.$(OBJEXT) \
 	main.$(OBJEXT) mm.$(OBJEXT) object.$(OBJEXT) pkt.$(OBJEXT) \
 	pres.$(OBJEXT) sess.$(OBJEXT) sm.$(OBJEXT) user.$(OBJEXT) \
-	storage.$(OBJEXT) storage_db.$(OBJEXT) storage_fs.$(OBJEXT) \
+	storage.$(OBJEXT) storage_db.$(OBJEXT) storage_ldapvcard.$(OBJEXT) storage_fs.$(OBJEXT) \
 	storage_mysql.$(OBJEXT) storage_pgsql.$(OBJEXT) \
 	mod_active.$(OBJEXT) mod_announce.$(OBJEXT) \
 	mod_deliver.$(OBJEXT) mod_disco.$(OBJEXT) \
 	mod_disco_publish.$(OBJEXT) mod_echo.$(OBJEXT) \
 	mod_help.$(OBJEXT) mod_offline.$(OBJEXT) mod_presence.$(OBJEXT) \
-	mod_privacy.$(OBJEXT) mod_roster.$(OBJEXT) \
+	mod_privacy.$(OBJEXT) mod_roster.$(OBJEXT) mod_roster_publish.$(OBJEXT) \
 	mod_session.$(OBJEXT) mod_vacation.$(OBJEXT) \
 	mod_validate.$(OBJEXT) mod_iq_last.$(OBJEXT) \
 	mod_iq_private.$(OBJEXT) mod_iq_time.$(OBJEXT) \
@@ -194,6 +196,7 @@
 @AMDEP_TRUE@	./$(DEPDIR)/mod_presence.Po \
 @AMDEP_TRUE@	./$(DEPDIR)/mod_privacy.Po \
 @AMDEP_TRUE@	./$(DEPDIR)/mod_roster.Po \
+@AMDEP_TRUE@    ./$(DEPDIR)/mod_roster_publish.Po \
 @AMDEP_TRUE@	./$(DEPDIR)/mod_session.Po \
 @AMDEP_TRUE@	./$(DEPDIR)/mod_template_roster.Po \
 @AMDEP_TRUE@	./$(DEPDIR)/mod_vacation.Po \
@@ -201,6 +204,7 @@
 @AMDEP_TRUE@	./$(DEPDIR)/pkt.Po ./$(DEPDIR)/pres.Po \
 @AMDEP_TRUE@	./$(DEPDIR)/sess.Po ./$(DEPDIR)/sm.Po \
 @AMDEP_TRUE@	./$(DEPDIR)/storage.Po ./$(DEPDIR)/storage_db.Po \
+@AMDEP_TRUE@    ./$(DEPDIR)/storage.Po ./$(DEPDIR)/storage_ldapvcard.Po \
 @AMDEP_TRUE@	./$(DEPDIR)/storage_fs.Po \
 @AMDEP_TRUE@	./$(DEPDIR)/storage_mysql.Po \
 @AMDEP_TRUE@	./$(DEPDIR)/storage_pgsql.Po ./$(DEPDIR)/user.Po
@@ -287,6 +291,7 @@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_presence.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_privacy.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_roster.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_roster_publish.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_session.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_template_roster.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_vacation.Po@am__quote@
@@ -298,6 +303,7 @@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sm.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/storage.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/storage_db.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/storage_ldapvcard.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/storage_fs.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/storage_mysql.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/storage_pgsql.Po@am__quote@
diff -Naur sm/mm.c sm/mm.c
--- sm/mm.c	2004-05-31 17:31:05.000000000 -0400
+++ sm/mm.c	2005-06-30 09:54:27.000000000 -0400
@@ -44,6 +44,7 @@
 extern int presence_init(mod_instance_t);
 extern int privacy_init(mod_instance_t);
 extern int roster_init(mod_instance_t);
+extern int roster_publish_init(mod_instance_t);
 extern int session_init(mod_instance_t);
 extern int vacation_init(mod_instance_t);
 extern int validate_init(mod_instance_t);
@@ -66,6 +67,7 @@
     "presence",
     "privacy",
     "roster",
+    "roster-publish",
     "session",
     "vacation",
     "validate",
@@ -90,6 +92,7 @@
     presence_init,
     privacy_init,
     roster_init,
+    roster_publish_init,
     session_init,
     vacation_init,
     validate_init,
diff -Naur sm/mod_roster.c sm/mod_roster.c
--- sm/mod_roster.c	2004-05-31 17:31:05.000000000 -0400
+++ sm/mod_roster.c	2005-06-30 09:54:27.000000000 -0400
@@ -593,6 +593,16 @@
 
     log_debug(ZONE, "loading roster for %s", jid_user(user->jid));
 
+    if( user->sm->sess_pending_jid ) {
+        if( jid_compare_full(user->jid,user->sm->sess_pending_jid) != 0 ) {
+            log_debug(ZONE,"roster: will not load, session opened for (%s) but not (%s)",jid_user(user->sm->sess_pending_jid),jid_user(user->jid));
+            return 0;
+        }
+    } else {
+        log_debug(ZONE,"roster: will not load, sess_pending_jid is not defined");
+        return 0;
+    }
+    
     user->roster = xhash_new(101);
 
     /* pull all the items */
diff -Naur sm/mod_roster_publish.c sm/mod_roster_publish.c
--- sm/mod_roster_publish.c	1969-12-31 19:00:00.000000000 -0500
+++ sm/mod_roster_publish.c	2005-06-30 09:54:27.000000000 -0400
@@ -0,0 +1,327 @@
+/*
+ * jabberd - Jabber Open Source Server
+ * Copyright (c) 2002 Jeremie Miller, Thomas Muldowney,
+ *                    Ryan Eatmon, Robert Norris
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA
+ */
+
+#include "sm.h"
+
+/** @file sm/mod_roster_publish.c
+  * @brief roster publishing
+  * @author Nikita Smirnov
+  */
+
+typedef struct _roster_publish_st {
+    int publish, forcegroups, fixsubs;
+    char *groupprefix, *groupsuffix, *removedomain;
+    int groupprefixlen, groupsuffixlen;
+} *roster_publish_t;
+
+/** free a single roster item */
+static void _roster_publish_free_walker(xht roster, const char *key, void *val, void *arg)
+{
+    item_t item = (item_t) val;
+    int i;
+
+    jid_free(item->jid);
+    
+    if(item->name != NULL)
+        free(item->name);
+
+    for(i = 0; i < item->ngroups; i++)
+        free(item->groups[i]);
+    free(item->groups);
+
+    free(item);
+}
+
+static void _roster_publish_save_item(user_t user, item_t item) {
+    os_t os;
+    os_object_t o;
+    char filter[4096];
+    int i;
+
+    log_debug(ZONE, "saving roster item %s for %s", jid_full(item->jid), jid_user(user->jid));
+
+    os = os_new();
+    o = os_object_new(os);
+
+    os_object_put(o, "jid", jid_full(item->jid), os_type_STRING);
+
+    if(item->name != NULL)
+        os_object_put(o, "name", item->name, os_type_STRING);
+
+    os_object_put(o, "to", &item->to, os_type_BOOLEAN);
+    os_object_put(o, "from", &item->from, os_type_BOOLEAN);
+    os_object_put(o, "ask", &item->ask, os_type_INTEGER);
+
+    snprintf(filter, 4096, "(jid=%s)", jid_full(item->jid));
+
+    storage_replace(user->sm->st, "roster-items", jid_user(user->jid), filter, os);
+
+    os_free(os);
+
+    snprintf(filter, 4096, "(jid=%s)", jid_full(item->jid));
+
+    if(item->ngroups == 0) {
+        storage_delete(user->sm->st, "roster-groups", jid_user(user->jid), filter);
+        return;
+    }
+
+    os = os_new();
+    
+    for(i = 0; i < item->ngroups; i++) {
+        o = os_object_new(os);
+
+        os_object_put(o, "jid", jid_full(item->jid), os_type_STRING);
+        os_object_put(o, "group", item->groups[i], os_type_STRING);
+    }
+
+    storage_replace(user->sm->st, "roster-groups", jid_user(user->jid), filter, os);
+
+    os_free(os);
+}
+
+/** publish the roster from the database */
+static int _roster_publish_user_load(mod_instance_t mi, user_t user) {
+    roster_publish_t roster_publish = (roster_publish_t) mi->mod->private;
+    os_t os, os_active;
+    os_object_t o;
+    os_type_t ot;
+    char *str, *group, filter[4096];
+    int i,j,gpos,found,delete,checksm,userinsm,tmp_to,tmp_from;
+    user_t usersm;
+    item_t item;
+    jid_t jid;
+
+    /* update roster to match published roster */
+    if( roster_publish->publish && user->sm->sess_pending_jid ) {
+        /* free if necessary */
+        if(user->roster == NULL) {
+            log_write(user->sm->log, LOG_NOTICE, "roster_publish: no roster for %s",jid_user(user->jid));
+            return 0;
+        }
+        if( jid_compare_full(user->jid,user->sm->sess_pending_jid) == 0 ) {
+//log_write(user->sm->log, LOG_NOTICE, "publishing roster for %s",jid_user(user->jid));
+            /* get published roster */
+            if( storage_get(user->sm->st, "published-roster", NULL, NULL, &os) == st_SUCCESS ) {
+                if(os_iter_first(os)) {
+                    /* iterate on published roster */
+                    jid = NULL;
+                    do {
+                        o = os_iter_object(os);
+                        if(os_object_get(o, "jid", (void **) &str, &ot)) {
+                            if( strcmp(str,jid_user(user->jid)) == 0 ) {
+                                /* not adding self */
+                                continue; /* do { } while( os_iter_next ) */
+                            }
+                            /* check that published item exists in sm database */
+                            checksm=0;
+                            if( jid ) jid_free(jid);
+                            jid = jid_new(user->sm->pc, str, strlen(str));
+                            if( roster_publish->removedomain ) {
+                                if( strcmp(jid->domain,roster_publish->removedomain) == 0 ) {
+                                    checksm = 1;
+                                }
+                            }
+                            if( checksm ) {
+                                /* is this a hack? but i whant to know - the user activated in sm or no? */
+                                if(storage_get(user->sm->st, "active", jid_user(jid), NULL, &os_active) == st_SUCCESS
+                                        && os_iter_first(os_active)) {
+                                    os_free(os_active);
+                                    userinsm = 1;
+                                } else {
+                                    userinsm = 0;
+                                }
+                            }
+                            item = xhash_get(user->roster,jid_user(jid));
+                            if( item == NULL ) {
+                                /* user has no this jid in his roster */
+                                /* if we checking sm database and user is not in it, not adding */
+                                if( checksm && !userinsm ) {
+                                    log_debug(ZONE, "published user %s has no record in sm, not adding", jid_user(jid));
+                                    continue; /* do { } while( os_iter_next ) */
+                                }
+                                log_debug(ZONE, "user has no %s in roster, adding", jid_user(jid));
+                                item = (item_t) malloc(sizeof(struct item_st));
+                                memset(item, 0, sizeof(struct item_st));
+
+                                item->jid = jid_new(mi->mod->mm->sm->pc, jid_user(jid), 0);
+                                if(item->jid == NULL) {
+                                    log_debug(ZONE, "eek! invalid jid %s, skipping it", jid_user(jid));
+                                    /* nvs: is it needed? */
+                                    free(item);
+                                    /* nvs: is it needed? */
+                                } else {
+                                    if(os_object_get(o, "name", (void **) &str, &ot))
+                                        item->name = strdup(str);
+
+                                    os_object_get(o, "to", (void **) &item->to, &ot);
+                                    os_object_get(o, "from", (void **) &item->from, &ot);
+                                    os_object_get(o, "ask", (void **) &item->ask, &ot);
+
+                                    os_object_get(o, "group", (void **) &str, &ot);
+                                    item->groups = realloc(item->groups, sizeof(char *) * (item->ngroups + 1));
+                                    item->groups[item->ngroups] = strdup(str);
+                                    item->ngroups++;
+
+                                    log_debug(ZONE, "adding %s to roster from template (to %d from %d ask %d name %s)", jid_full(item->jid), item->to, item->from, item->ask, item->name);
+
+                                    /* its good */
+                                    xhash_put(user->roster, jid_full(item->jid), (void *) item);
+                                    _roster_publish_save_item(user,item);
+                                }
+                            }
+                            else /* if( item == NULL ) else ... : user has this jid in his roster */
+                            {
+                                /* if we checking sm database and user is not in it, remove it from roster */
+                                if( checksm && !userinsm ) {
+                                    log_debug(ZONE, "published user %s has no record in sm, deleting from roster", jid_user(jid));
+                                    
+                                    snprintf(filter, 4096, "(jid=%s)", jid_full(jid));
+                                    storage_delete(user->sm->st, "roster-items", jid_user(user->jid), filter);
+                                    snprintf(filter, 4096, "(jid=%s)", jid_full(jid));
+                                    storage_delete(user->sm->st, "roster-groups", jid_user(user->jid), filter);
+
+                                    xhash_zap(user->roster, jid_full(jid));
+                                    _roster_publish_free_walker(NULL, (const char *) jid_full(jid), (void *) item, NULL);
+                                    continue; /* do { } while( os_iter_next ) */
+                                }
+                                if( roster_publish->fixsubs ) {
+                                    /* check subscriptions and correct if needed */
+                                    os_object_get(o, "to", (void **) &tmp_to, &ot);
+                                    os_object_get(o, "from", (void **) &tmp_from, &ot);
+                                    if( item->to != tmp_to || item->from != tmp_from ) {
+                                        item->to = tmp_to;
+                                        item->from = tmp_from;
+                                        log_debug(ZONE, "fixsubs in roster %s, item %s",jid_user(user->jid),jid_user(item->jid));
+                                        xhash_put(user->roster, jid_full(item->jid), (void *) item);
+                                        _roster_publish_save_item(user,item);
+                                    }
+                                }
+                                if( roster_publish->forcegroups ) {
+                                    /* item already in roster, check groups if needed */
+                                    os_object_get(o, "group", (void **) &group, &ot);
+                                    /* find published roster item's group in user's roster */
+                                    found = 0;
+                                    if( group ) {
+                                        for(i = 0; i < item->ngroups; i++) {
+                                            if( strcmp(item->groups[i],group) == 0 ) {
+                                                found = 1;
+                                                /* do not break loop, give groups that matches
+                                                 * prefix and suffix to be deleted
+                                                 */
+                                            } else {
+                                                /* check if user's roster group matches
+                                                 * prefix or suffix given in config
+                                                 * and delete such groups (and thus they will be replaced)
+                                                 */
+                                                delete = 0;
+                                                if( roster_publish->groupprefix ) {
+                                                    if( strncmp(item->groups[i],roster_publish->groupprefix,roster_publish->groupprefixlen) == 0 ) {
+                                                        delete = 1;
+                                                    }
+                                                }
+                                                if( !delete && roster_publish->groupsuffix ) {
+                                                    gpos=strlen(item->groups[i])-roster_publish->groupsuffixlen;
+                                                    if( gpos > 0 ) {
+                                                        if( strcmp(item->groups[i]+gpos,roster_publish->groupsuffix) == 0 ) {
+                                                            delete = 1;
+                                                        }
+                                                    }
+                                                }
+                                                /* remove group from roster item */
+                                                if( delete ) {
+                                                    free(item->groups[i]);
+                                                    for(j = i; j < item->ngroups-1; j++) {
+                                                        item->groups[j]=item->groups[j+1];
+                                                    }
+                                                    item->ngroups--;
+                                                    item->groups = realloc(item->groups, sizeof(char *) * (item->ngroups));
+                                                }
+                                            }
+                                        } /* for(i... */
+                                    } /* if( group ) */
+                                    if( !found ) {
+                                        log_debug(ZONE, "adding group %s to item %s for user %s",group,jid_user(item->jid),jid_user(user->jid));
+                                        item->groups = realloc(item->groups, sizeof(char *) * (item->ngroups + 1));
+                                        item->groups[item->ngroups] = strdup(group);
+                                        item->ngroups++;
+                                        /* replace item */
+                                        xhash_put(user->roster, jid_full(item->jid), (void *) item);
+                                        _roster_publish_save_item(user,item);
+                                    }
+                                } /* else if( roster_publish->forcegroups ) */
+                            } /* end of if if( item == NULL ) */
+                        } /* if( os_object_get(...) */
+                    } while(os_iter_next(os));
+                    if( jid ) jid_free(jid);
+                }
+                os_free(os);
+            } else {
+                log_debug(ZONE,"cannot read published roster");
+            }
+        }
+//log_write(user->sm->log, LOG_ERR, "roster_publish: publisheded roster for %s",jid_user(user->jid));
+    }
+    return 0;
+}
+
+static void _roster_publish_free(module_t mod) {
+    roster_publish_t roster_publish = (roster_publish_t) mod->private;
+
+    free(roster_publish);
+}
+
+int roster_publish_init(mod_instance_t mi, char *arg) {
+    log_debug(ZONE,"roster_publish_init called");
+    
+    module_t mod = mi->mod;
+    roster_publish_t roster_publish;
+
+    if(mod->init) return 0;
+
+    roster_publish = (roster_publish_t) malloc(sizeof(struct _roster_publish_st));
+    memset(roster_publish, 0, sizeof(struct _roster_publish_st));
+
+    if( config_get_one(mod->mm->sm->config, "user.template.publish", 0) ) {
+	log_debug(ZONE,"publish template is set");
+        roster_publish->publish = 1;
+        roster_publish->removedomain = config_get_one(mod->mm->sm->config, "user.template.publish.check-remove-domain", 0);
+        roster_publish->fixsubs = j_atoi(config_get_one(mod->mm->sm->config, "user.template.publish.fix-subscriptions", 0), 0);
+        if( config_get_one(mod->mm->sm->config, "user.template.publish.force-groups", 0) ) {
+            roster_publish->forcegroups = 1;
+            if( roster_publish->groupprefix = config_get_one(mod->mm->sm->config, "user.template.publish.force-groups.prefix", 0) ) {
+                roster_publish->groupprefixlen = strlen(roster_publish->groupprefix);
+            }
+            if( roster_publish->groupsuffix = config_get_one(mod->mm->sm->config, "user.template.publish.force-groups.suffix", 0) ) {
+                roster_publish->groupsuffixlen = strlen(roster_publish->groupsuffix);
+            }
+        } else {
+            roster_publish->forcegroups = 0;
+        }
+    } else {
+	log_debug(ZONE,"publish template is NOT set");
+        roster_publish->publish = 0;
+    }
+    mod->private = roster_publish;
+
+    mod->user_load = _roster_publish_user_load;
+    mod->free = _roster_publish_free;
+
+    return 0;
+}
diff -Naur sm/sess.c sm/sess.c
--- sm/sess.c	2004-05-31 17:31:05.000000000 -0400
+++ sm/sess.c	2005-06-30 09:54:27.000000000 -0400
@@ -105,8 +105,13 @@
     log_debug(ZONE, "session requested for %s", jid_full(jid));
 
     /* get user data for this guy */
+
+    sm->sess_pending_jid = jid;
+
     user = user_load(sm, jid);
 
+    sm->sess_pending_jid = NULL;
+
     /* unknown user */
     if(user == NULL) {
         if(config_get(sm->config, "user.auto-create") == NULL) {
@@ -119,7 +124,12 @@
         if(user_create(sm, jid) != 0)
             return NULL;
 
+	sm->sess_pending_jid = jid;
+
         user = user_load(sm, jid);
+
+	sm->sess_pending_jid = NULL;
+	
         if(user == NULL) {
             log_write(sm->log, LOG_NOTICE, "couldn't load user, can't start session: jid=%s", jid_full(jid));
             return NULL;
diff -Naur sm/sm.h sm/sm.h
--- sm/sm.h	2004-05-31 17:31:05.000000000 -0400
+++ sm/sm.h	2005-06-30 09:54:27.000000000 -0400
@@ -187,6 +187,8 @@
 
     xht                 users;              /**< pointers to currently loaded users (key is user@@domain) */
 
+    jid_t               sess_pending_jid;   /**< jid for which session is created (set before user_load in sess.c and unset after user_load) */
+
     xht                 sessions;           /**< pointers to all connected sessions (key is random sm id) */
 
     xht                 xmlns;              /**< index of common namespaces (for iq sub-namespace in pkt_t) */
diff -Naur sm/storage.c sm/storage.c
--- sm/storage.c	2004-05-31 17:31:05.000000000 -0400
+++ sm/storage.c	2005-06-30 09:54:27.000000000 -0400
@@ -36,6 +36,9 @@
 #ifdef STORAGE_DB
 extern st_ret_t st_db_init(st_driver_t);
 #endif
+#ifdef STORAGE_LDAPVCARD
+extern st_ret_t st_ldapvcard_init(st_driver_t);
+#endif
 #ifdef STORAGE_FS
 extern st_ret_t st_fs_init(st_driver_t);
 #endif
@@ -50,6 +53,9 @@
 #ifdef STORAGE_DB
     "db",
 #endif
+#ifdef STORAGE_LDAPVCARD
+    "ldapvcard",
+#endif
 #ifdef STORAGE_FS
     "fs",
 #endif
@@ -66,6 +72,9 @@
 #ifdef STORAGE_DB
     st_db_init,
 #endif
+#ifdef STORAGE_LDAPVCARD
+    st_ldapvcard_init,
+#endif
 #ifdef STORAGE_FS
     st_fs_init,
 #endif
diff -Naur sm/storage_ldapvcard.c sm/storage_ldapvcard.c
--- sm/storage_ldapvcard.c	1969-12-31 19:00:00.000000000 -0500
+++ sm/storage_ldapvcard.c	2005-06-30 09:56:02.000000000 -0400
@@ -0,0 +1,373 @@
+/*
+ * jabberd - Jabber Open Source Server
+ * Copyright (c) 2002 Jeremie Miller, Thomas Muldowney,
+ *                    Ryan Eatmon, Robert Norris
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA
+ */
+
+/*
+ * Written by Nikita Smirnov in 2004
+ * on basis of authreg_ldap.c and storage_fs.c
+ */
+
+#include "sm.h"
+
+#ifdef STORAGE_LDAPVCARD
+
+#include <ldap.h>
+
+/** internal structure, holds our data */
+typedef struct drvdata_st {
+    LDAP *ld;
+    char *uri;
+    char *srvid;
+    char *binddn;
+    char *bindpw;
+    char *basedn;
+
+    char *objectclass;
+    char *uidattr;
+    char *pwattr;
+    char *groupattr; // group for published-roster
+    char *publishedattr; // cat we publish it?
+} *drvdata_t;
+
+typedef struct {
+    char *ldapentry, *vcardentry;
+    os_type_t ot;
+} ldapvcard_entry_st;
+
+ldapvcard_entry_st ldapvcard_entry[] =
+{
+    {"displayName","fn",os_type_STRING},
+    {"cn","nickname",os_type_STRING},
+    {"labeledURI","url",os_type_STRING},
+    {"telephoneNumber","tel",os_type_STRING},
+    {"mail","email",os_type_STRING},
+    {"title","title",os_type_STRING},
+//    {"","role",os_type_STRING},
+//    {"","bday",os_type_STRING},
+    {"description","desc",os_type_STRING},
+    {"givenName","n-given",os_type_STRING},
+    {"sn","n-family",os_type_STRING},
+    {"st","adr-street",os_type_STRING},
+    {"l","adr-locality",os_type_STRING},
+//    {"","adr-region",os_type_STRING},
+    {"postalCode","adr-pcode",os_type_STRING},
+    {"c","adr-country",os_type_STRING},
+    {"o","org-orgname",os_type_STRING},
+    {"ou","org-orgunit",os_type_STRING},
+    {NULL,NULL,0}
+};
+
+/** utility function to get ld_errno */
+static int _st_ldapvcard_get_lderrno(LDAP *ld)
+{
+  int ld_errno;
+  ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &ld_errno);
+  return ld_errno;
+}
+
+/** connect to the ldap host */
+static int _st_ldapvcard_connect(st_driver_t drv)
+{
+  drvdata_t data = (drvdata_t) drv->private;
+  int ldapversion = LDAP_VERSION3;
+  int rc;
+
+  if(data->ld != NULL)
+    ldap_unbind_s(data->ld);
+
+  rc = ldap_initialize( &(data->ld), data->uri);
+  if( rc != LDAP_SUCCESS )
+  {
+    log_write(drv->st->sm->log, LOG_ERR, "ldap: ldap_initialize failed (uri=%s): %s", data->uri, ldap_err2string(rc));
+    return 1;
+  }
+
+  if (ldap_set_option(data->ld, LDAP_OPT_PROTOCOL_VERSION, &ldapversion) != LDAP_SUCCESS)
+  {
+    log_write(drv->st->sm->log, LOG_ERR, "ldap: couldn't set v3 protocol");
+    return 1;
+  }
+  if (ldap_set_option(data->ld, LDAP_OPT_REFERRALS, LDAP_OPT_ON) != LDAP_SUCCESS)
+  {
+    log_write(drv->st->sm->log, LOG_ERR, "ldap: couldn't set LDAP_OPT_REFERRALS");
+  }
+
+  return 0;
+}
+
+/** unbind and clear variables */
+static int _st_ldapvcard_unbind(st_driver_t drv) {
+  drvdata_t data = (drvdata_t) drv->private;
+  ldap_unbind_s(data->ld);
+  data->ld = NULL;
+  return 0;
+}
+
+/** connect to ldap and bind as data->binddn */
+static int _st_ldapvcard_connect_bind(st_driver_t drv) {
+  drvdata_t data = (drvdata_t) drv->private;
+
+  if(data->ld != NULL ) {
+    return 0;
+  }
+
+  if( _st_ldapvcard_connect(drv) ) {
+    return 1;
+  }
+  if(ldap_simple_bind_s(data->ld, data->binddn, data->bindpw))
+  {
+    log_write(drv->st->sm->log, LOG_ERR, "ldap: bind as %s failed: %s", data->binddn, ldap_err2string(_st_ldapvcard_get_lderrno(data->ld)));
+    _st_ldapvcard_unbind(drv);
+    return 1;
+  }
+  return 0;
+}
+
+static st_ret_t _st_ldapvcard_add_type(st_driver_t drv, char *type) {
+    if( strncmp(type,"vcard",6) != 0 && strncmp(type,"published-roster",17) ) {
+        log_write(drv->st->sm->log, LOG_ERR, "ldap: only vcard and published-roster types supperted for now");
+        return st_FAILED;
+    } else {
+        return st_SUCCESS;
+    }
+
+    return st_SUCCESS;
+}
+
+static st_ret_t _st_ldapvcard_get(st_driver_t drv, char *type, char *owner, char *filter, os_t *os) {
+    drvdata_t data = (drvdata_t) drv->private;
+    os_object_t o;
+    char ldapfilter[1024], *no_attrs[] = { NULL }, **vals;
+    LDAPMessage *result, *entry;
+    ldapvcard_entry_st le;
+    int i,ival;
+    char jid[2048], group[1024], name[2048]; // name is cn[1024] + ' ' + initials[1024]
+
+    if( _st_ldapvcard_connect_bind(drv) ) {
+        return st_FAILED;
+    }
+
+    if( strncmp(type,"vcard",6) == 0 ) {
+	/* The owner will be uid@server, so we need to strip it back to uid to search for it in LDAP */
+	/* This method means your uid can't contain "@" */
+	char *atpos;
+	atpos = strtok(owner,"@");
+	    
+        snprintf(ldapfilter, 1024, "(&(objectClass=%s)(%s=%s))", data->objectclass, data->uidattr, atpos);
+        if(ldap_search_s(data->ld, data->basedn, LDAP_SCOPE_SUBTREE, ldapfilter, no_attrs, 0, &result))
+        {
+            log_write(drv->st->sm->log, LOG_ERR, "ldap: search %s failed: %s", ldapfilter, ldap_err2string(_st_ldapvcard_get_lderrno(data->ld)));
+            _st_ldapvcard_unbind(drv);
+            return st_FAILED;
+        }
+
+        entry = ldap_first_entry(data->ld, result);
+        if(entry == NULL)
+        {
+            ldap_msgfree(result);
+            return st_FAILED;
+        }
+
+        *os = os_new();
+
+        o = os_object_new(*os);
+
+        i = 0;
+        le = ldapvcard_entry[i];
+        while( le.ldapentry != NULL ) {
+            vals=ldap_get_values(data->ld,entry,le.ldapentry);
+            if( ldap_count_values(vals) > 0 ) {
+                switch(le.ot) {
+                    case os_type_BOOLEAN:
+                    case os_type_INTEGER:
+                        ival=atoi(vals[0]);
+                        os_object_put(o, le.vcardentry, &ival, le.ot);
+                        break;
+                    case os_type_STRING:
+                        os_object_put(o, le.vcardentry, vals[0], le.ot);
+                        break;
+
+                }
+            }
+            ldap_value_free(vals);
+            le = ldapvcard_entry[++i];
+        }
+        ldap_msgfree(result);
+    } else if( strncmp(type,"published-roster",17) == 0 ) {
+        /* snprintf(ldapfilter, 1024, "(&(&(%s=*)(!(%s=0)))(objectClass=%s)(%s=*))", data->publishedattr, data->publishedattr, data->objectclass, data->uidattr); */
+        snprintf(ldapfilter, 1024, "(&(objectClass=%s)(%s=*))", data->objectclass, data->uidattr);
+	if(ldap_search_s(data->ld, data->basedn, LDAP_SCOPE_SUBTREE, ldapfilter, no_attrs, 0, &result))
+        {
+            log_write(drv->st->sm->log, LOG_ERR, "ldap: search %s failed: %s", ldapfilter, ldap_err2string(_st_ldapvcard_get_lderrno(data->ld)));
+            _st_ldapvcard_unbind(drv);
+            return st_FAILED;
+        }
+
+        entry = ldap_first_entry(data->ld, result);
+        if(entry == NULL)
+        {
+            ldap_msgfree(result);
+            return st_FAILED;
+        }
+
+        *os = os_new();
+
+        do {
+            vals = ldap_get_values(data->ld,entry,data->groupattr);
+            if( ldap_count_values(vals) <= 0 ) {
+                ldap_value_free(vals);
+                continue;
+            }
+            strncpy(group,vals[0],sizeof(group)-1); group[sizeof(group)-1]='\0';
+            ldap_value_free(vals);
+            
+            vals = ldap_get_values(data->ld,entry,data->uidattr);
+            if( ldap_count_values(vals) <= 0 ) {
+                ldap_value_free(vals);
+                free(group);
+                continue;
+            }
+            strncpy(jid,vals[0],sizeof(jid)-1); jid[sizeof(jid)-1]='\0';
+            ldap_value_free(vals);
+            
+            vals = ldap_get_values(data->ld,entry,"cn");
+            if( ldap_count_values(vals) <= 0 ) {
+                ldap_value_free(vals);
+                vals = ldap_get_values(data->ld,entry,"displayName");
+                if( ldap_count_values(vals) <= 0 ) {
+                    strncpy(name,jid,sizeof(name)-1); name[sizeof(name)-1]='\0';
+                } else {
+                    strncpy(name,vals[0],sizeof(name)-1); name[sizeof(name)-1]='\0';
+                }
+            } else {
+                strncpy(name,vals[0],1023); name[1023]='\0';
+                ldap_value_free(vals);
+                vals = ldap_get_values(data->ld,entry,"initials");
+                if( ldap_count_values(vals) > 0 ) {
+                    strcat(name," ");
+                    strncat(name,vals[0],1023);
+                }
+            }
+            ldap_value_free(vals);
+
+	    /* We're using LDAP for authentication based on uid, so the user's jid will always be */
+	    /* uid@servername. So, append @servername onto jid before we store it in the roster. */
+	    strcat(jid,"@");
+	    strcat(jid,data->srvid);
+	    
+            o = os_object_new(*os);
+            os_object_put(o,"jid",jid,os_type_STRING);
+            os_object_put(o,"group",group,os_type_STRING);
+            os_object_put(o,"name",name,os_type_STRING);
+            ival=1;
+            os_object_put(o,"to",&ival,os_type_INTEGER);
+            os_object_put(o,"from",&ival,os_type_INTEGER);
+            ival=0;
+            os_object_put(o,"ask",&ival,os_type_INTEGER);
+        } while( entry = ldap_next_entry(data->ld, entry) );
+        ldap_msgfree(result);
+    } else {
+        log_write(drv->st->sm->log, LOG_ERR, "ldap: unknown storage type: '%s'", type);
+        return st_FAILED;
+    }
+
+    return st_SUCCESS;
+}
+
+static st_ret_t _st_ldapvcard_put(st_driver_t drv, char *type, char *owner, os_t os) {
+    return st_FAILED;
+}
+static st_ret_t _st_ldapvcard_delete(st_driver_t drv, char *type, char *owner, char *filter) {
+    return st_SUCCESS;
+}
+static st_ret_t _st_ldapvcard_replace(st_driver_t drv, char *type, char *owner, char *filter, os_t os) {
+    return st_FAILED;
+}
+
+static void _st_ldapvcard_free(st_driver_t drv) {
+    drvdata_t data = (drvdata_t) drv->private;
+    if( data->ld ) {
+        _st_ldapvcard_unbind(drv);
+    }
+    free(data);
+}
+
+st_ret_t st_ldapvcard_init(st_driver_t drv) {
+    drvdata_t data;
+    char *uri, *basedn, *serverid;
+
+    log_write(drv->st->sm->log, LOG_NOTICE, "ldap: initializing");
+
+    uri = config_get_one(drv->st->sm->config, "storage.ldapvcard.uri", 0);
+    if(uri == NULL) {
+        log_write(drv->st->sm->log, LOG_ERR, "ldap: no uri specified in config file");
+        return st_FAILED;
+    }
+
+    serverid = config_get_one(drv->st->sm->config, "id", 0);
+    if(serverid == NULL) {
+	log_write(drv->st->sm->log, LOG_ERR, "ldap: no server id specified in config file");
+	return st_FAILED;
+    }
+
+    basedn = config_get_one(drv->st->sm->config, "storage.ldapvcard.basedn", 0);
+    if(basedn == NULL) {
+        log_write(drv->st->sm->log, LOG_ERR, "ldap: no basedns specified in config file");
+        return st_FAILED;
+    }
+    data = (drvdata_t) malloc(sizeof(struct drvdata_st));
+    memset(data, 0, sizeof(struct drvdata_st));
+
+    drv->private = (void *) data;
+
+    data->uri = uri;
+    data->basedn = basedn;
+    data->srvid = serverid;
+
+    data->binddn = config_get_one(drv->st->sm->config, "storage.ldapvcard.binddn", 0);
+    if(data->binddn != NULL)
+        data->bindpw = config_get_one(drv->st->sm->config, "storage.ldapvcard.bindpw", 0);
+
+    data->uidattr = config_get_one(drv->st->sm->config, "storage.ldapvcard.uidattr", 0);
+    if(data->uidattr == NULL)
+        data->uidattr = "uid";
+
+    data->groupattr = config_get_one(drv->st->sm->config, "storage.ldapvcard.groupattr", 0);
+    if(data->groupattr == NULL)
+        data->groupattr = "jabberPublishedGroup";
+    
+    data->publishedattr = config_get_one(drv->st->sm->config, "storage.ldapvcard.publishedattr", 0);
+    if(data->publishedattr == NULL)
+        data->publishedattr = "jabberPublishedItem";
+    
+    data->objectclass = config_get_one(drv->st->sm->config, "storage.ldapvcard.objectclass", 0);
+    if(data->objectclass == NULL)
+        data->objectclass = "jabberUser";
+
+    drv->add_type = _st_ldapvcard_add_type;
+    drv->put = _st_ldapvcard_put;
+    drv->get = _st_ldapvcard_get;
+    drv->delete = _st_ldapvcard_delete;
+    drv->replace = _st_ldapvcard_replace;
+    drv->free = _st_ldapvcard_free;
+
+    return st_SUCCESS;
+}
+
+#endif
