server: Retrieve the groups for a token from the server.
diff --git a/server/protocol.def b/server/protocol.def
index ef9acb1..e08db4c 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -193,6 +193,13 @@
     /* VARARGS(dacl,ACL); */
 };
 
+struct token_groups
+{
+    unsigned int count;
+    /* unsigned int attributes[count]; */
+    /* VARARGS(sids,SID); */
+};
+
 /****************************************************************/
 /* Request declarations */
 
@@ -2509,6 +2516,13 @@
     VARARG(user,SID);             /* sid of the user the token represents */
 @END
 
+@REQ(get_token_groups)
+    obj_handle_t    handle;       /* handle to the token */
+@REPLY
+    size_t          user_len;     /* length needed to store user */
+    VARARG(user,token_groups); /* groups the token's user belongs to */
+@END
+
 /* Create a mailslot */
 @REQ(create_mailslot)
     unsigned int   access;        /* wanted access rights */
diff --git a/server/request.h b/server/request.h
index f6df040..5b69712 100644
--- a/server/request.h
+++ b/server/request.h
@@ -314,6 +314,7 @@
 DECL_HANDLER(duplicate_token);
 DECL_HANDLER(access_check);
 DECL_HANDLER(get_token_user);
+DECL_HANDLER(get_token_groups);
 DECL_HANDLER(create_mailslot);
 DECL_HANDLER(open_mailslot);
 DECL_HANDLER(set_mailslot_info);
@@ -532,6 +533,7 @@
     (req_handler)req_duplicate_token,
     (req_handler)req_access_check,
     (req_handler)req_get_token_user,
+    (req_handler)req_get_token_groups,
     (req_handler)req_create_mailslot,
     (req_handler)req_open_mailslot,
     (req_handler)req_set_mailslot_info,
diff --git a/server/token.c b/server/token.c
index aedba6a..c7c5970 100644
--- a/server/token.c
+++ b/server/token.c
@@ -93,7 +93,7 @@
     unsigned    def      : 1; /* is the privilege enabled by default? */
 };
 
-struct sid_and_attributes
+struct group
 {
     struct list entry;
     unsigned    enabled  : 1; /* is the sid currently enabled? */
@@ -397,7 +397,7 @@
 
     LIST_FOR_EACH_SAFE( cursor, cursor_next, &token->groups )
     {
-        struct sid_and_attributes *group = LIST_ENTRY( cursor, struct sid_and_attributes, entry );
+        struct group *group = LIST_ENTRY( cursor, struct group, entry );
         list_remove( &group->entry );
         free( group );
     }
@@ -436,8 +436,8 @@
         /* copy groups */
         for (i = 0; i < group_count; i++)
         {
-            size_t size = FIELD_OFFSET( struct sid_and_attributes, sid.SubAuthority[((const SID *)groups[i].Sid)->SubAuthorityCount] );
-            struct sid_and_attributes *group = mem_alloc( size );
+            size_t size = FIELD_OFFSET( struct group, sid.SubAuthority[((const SID *)groups[i].Sid)->SubAuthorityCount] );
+            struct group *group = mem_alloc( size );
 
             if (!group)
             {
@@ -696,11 +696,11 @@
 
 static int token_sid_present( struct token *token, const SID *sid, int deny )
 {
-    struct sid_and_attributes *group;
+    struct group *group;
 
     if (security_equal_sid( token->user, sid )) return TRUE;
 
-    LIST_FOR_EACH_ENTRY( group, &token->groups, struct sid_and_attributes, entry )
+    LIST_FOR_EACH_ENTRY( group, &token->groups, struct group, entry )
     {
         if (!group->enabled) continue;
         if (group->deny_only && !deny) continue;
@@ -1023,14 +1023,14 @@
         if (token)
         {
             struct privilege *privilege;
-            struct sid_and_attributes *group;
+            struct group *group;
             unsigned int access;
 
             /* copy groups */
-            LIST_FOR_EACH_ENTRY( group, &src_token->groups, struct sid_and_attributes, entry )
+            LIST_FOR_EACH_ENTRY( group, &src_token->groups, struct group, entry )
             {
-                size_t size = FIELD_OFFSET( struct sid_and_attributes, sid.SubAuthority[group->sid.SubAuthorityCount] );
-                struct sid_and_attributes *newgroup = mem_alloc( size );
+                size_t size = FIELD_OFFSET( struct group, sid.SubAuthority[group->sid.SubAuthorityCount] );
+                struct group *newgroup = mem_alloc( size );
                 if (!newgroup)
                 {
                     release_object( token );
@@ -1132,7 +1132,7 @@
     }
 }
 
-/* */
+/* retrives the SID of the user that the token represents */
 DECL_HANDLER(get_token_user)
 {
     struct token *token;
@@ -1157,3 +1157,61 @@
         release_object( token );
     }
 }
+
+/* retrieves the groups that the user represented by the token belongs to */
+DECL_HANDLER(get_token_groups)
+{
+    struct token *token;
+
+    reply->user_len = 0;
+
+    if ((token = (struct token *)get_handle_obj( current->process, req->handle,
+                                                 TOKEN_QUERY,
+                                                 &token_ops )))
+    {
+        size_t size_needed = sizeof(struct token_groups);
+        unsigned int group_count = 0;
+        const struct group *group;
+
+        LIST_FOR_EACH_ENTRY( group, &token->groups, const struct group, entry )
+        {
+            group_count++;
+            size_needed += FIELD_OFFSET(SID, SubAuthority[group->sid.SubAuthorityCount]);
+        }
+        size_needed += sizeof(unsigned int) * group_count;
+
+        reply->user_len = size_needed;
+
+        if (size_needed <= get_reply_max_size())
+        {
+            struct token_groups *tg = set_reply_data_size( size_needed );
+            if (tg)
+            {
+                unsigned int *attr_ptr = (unsigned int *)(tg + 1);
+                SID *sid_ptr = (SID *)(attr_ptr + group_count);
+
+                tg->count = group_count;
+
+                LIST_FOR_EACH_ENTRY( group, &token->groups, const struct group, entry )
+                {
+
+                    *attr_ptr = 0;
+                    if (group->mandatory) *attr_ptr |= SE_GROUP_MANDATORY;
+                    if (group->def) *attr_ptr |= SE_GROUP_ENABLED_BY_DEFAULT;
+                    if (group->enabled) *attr_ptr |= SE_GROUP_ENABLED;
+                    if (group->owner) *attr_ptr |= SE_GROUP_OWNER;
+                    if (group->deny_only) *attr_ptr |= SE_GROUP_USE_FOR_DENY_ONLY;
+                    if (group->resource) *attr_ptr |= SE_GROUP_RESOURCE;
+
+                    memcpy(sid_ptr, &group->sid, FIELD_OFFSET(SID, SubAuthority[group->sid.SubAuthorityCount]));
+
+                    sid_ptr = (SID *)((char *)sid_ptr + FIELD_OFFSET(SID, SubAuthority[group->sid.SubAuthorityCount]));
+                    attr_ptr++;
+                }
+            }
+        }
+        else set_error( STATUS_BUFFER_TOO_SMALL );
+
+        release_object( token );
+    }
+}
diff --git a/server/trace.c b/server/trace.c
index 350b2ac..f4de26b 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -560,6 +560,43 @@
     remove_data( size );
 }
 
+static void dump_varargs_token_groups( size_t size )
+{
+    const struct token_groups *tg = cur_data;
+    fputc( '{', stderr );
+    if (size >= sizeof(struct token_groups))
+    {
+        size_t offset = sizeof(*tg);
+        fprintf( stderr, "count=%08x,", tg->count );
+        if (tg->count * sizeof(unsigned int) <= size)
+        {
+            unsigned int i;
+            const unsigned int *attr = (const unsigned int *)(tg + 1);
+
+            offset += tg->count * sizeof(unsigned int);
+
+            fputc( '[', stderr );
+            for (i = 0; i < tg->count; i++)
+            {
+                const SID *sid = (const SID *)((const char *)cur_data + offset);
+                if (i != 0)
+                    fputc( ',', stderr );
+                fputc( '{', stderr );
+                fprintf( stderr, "attributes=%08x", attr[i] );
+                fprintf( stderr, ",sid=" );
+                dump_inline_sid( sid, size - offset );
+                if ((offset + FIELD_OFFSET(SID, SubAuthority[0]) > size) ||
+                    (offset + FIELD_OFFSET(SID, SubAuthority[sid->SubAuthorityCount]) > size))
+                    break;
+                offset += FIELD_OFFSET(SID, SubAuthority[sid->SubAuthorityCount]);
+                fputc( '}', stderr );
+            }
+            fputc( ']', stderr );
+        }
+    }
+    fputc( '}', stderr );
+}
+
 typedef void (*dump_func)( const void *req );
 
 /* Everything below this line is generated automatically by tools/make_requests */
@@ -3096,6 +3133,18 @@
     dump_varargs_SID( cur_size );
 }
 
+static void dump_get_token_groups_request( const struct get_token_groups_request *req )
+{
+    fprintf( stderr, " handle=%p", req->handle );
+}
+
+static void dump_get_token_groups_reply( const struct get_token_groups_reply *req )
+{
+    fprintf( stderr, " user_len=%lu,", (unsigned long)req->user_len );
+    fprintf( stderr, " user=" );
+    dump_varargs_token_groups( cur_size );
+}
+
 static void dump_create_mailslot_request( const struct create_mailslot_request *req )
 {
     fprintf( stderr, " access=%08x,", req->access );
@@ -3418,6 +3467,7 @@
     (dump_func)dump_duplicate_token_request,
     (dump_func)dump_access_check_request,
     (dump_func)dump_get_token_user_request,
+    (dump_func)dump_get_token_groups_request,
     (dump_func)dump_create_mailslot_request,
     (dump_func)dump_open_mailslot_request,
     (dump_func)dump_set_mailslot_info_request,
@@ -3633,6 +3683,7 @@
     (dump_func)dump_duplicate_token_reply,
     (dump_func)dump_access_check_reply,
     (dump_func)dump_get_token_user_reply,
+    (dump_func)dump_get_token_groups_reply,
     (dump_func)dump_create_mailslot_reply,
     (dump_func)dump_open_mailslot_reply,
     (dump_func)dump_set_mailslot_info_reply,
@@ -3848,6 +3899,7 @@
     "duplicate_token",
     "access_check",
     "get_token_user",
+    "get_token_groups",
     "create_mailslot",
     "open_mailslot",
     "set_mailslot_info",