diff -ruNp ejudge-2.3.18/bitset.c ejudge/bitset.c
--- ejudge-2.3.18/bitset.c	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/bitset.c	2011-07-11 09:02:04.099281563 +0400
@@ -0,0 +1,395 @@
+/* -*- mode: c -*- */
+/* $Id: bitset.c 6355 2011-06-05 07:45:04Z cher $ */
+
+/* Copyright (C) 2011 Alexander Chernov <cher@ejudge.ru> */
+
+/*
+ * 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.
+ */
+
+#include "bitset.h"
+
+#include "reuse_xalloc.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+static unsigned char const base64_encode_table[]=
+"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_";
+static signed char const base64_decode_table[] =
+{
+  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1,
+   0,  1,  2,  3,  4,  5,  6,  7,  8,  9, -1, -1, -1, -1, -1, -1,
+  -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+  25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, 63,
+  -1, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
+  51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1,
+  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+};
+
+enum { ENCODE_BASE = 64 };
+enum { MAX_VALUE = 1000000000 };
+
+int
+bitset_url_decode(
+        const unsigned char *str,
+        bitset_t *ss)
+{
+  int i = 0, len, cnt, d, start, count, j;
+  int size = 0, alloc = 0;
+  unsigned char *set = 0;
+  int max_num = -1;
+
+  if (ss) {
+    ss->size = 0;
+    xfree(ss->set); ss->set = NULL;
+  }
+
+  if (!str || !*str) goto empty;
+  len = strlen(str);
+  if (len < 0 || len > MAX_VALUE) goto fail;
+
+  // count the size of the set and validate it
+  while (i < len) {
+    if (str[i] < '0' || str[i] > '9') goto fail;
+    if (str[i] == '0') {
+      ++i;
+      continue;
+    }
+    cnt = str[i++] - '0';
+    if (i + cnt > len) goto fail;
+    start = 0;
+    for (; cnt; --cnt, ++i) {
+      d = base64_decode_table[str[i]];
+      if (d < 0 || d >= ENCODE_BASE) goto fail;
+      if (start >= MAX_VALUE / ENCODE_BASE - 1) goto fail;
+      start = start * ENCODE_BASE + d;
+    }
+    if (start < 0 || start >= MAX_VALUE) goto fail;
+    if (str[i] < '0' || str[i] > '9') goto fail;
+    if (str[i] == '0') {
+      if (start > max_num) max_num = start;
+      ++i;
+      continue;
+    }
+    cnt = str[i++] - '0';
+    if (i + cnt > len) goto fail;
+    count = 0;
+    for (; cnt; --cnt, ++i) {
+      d = base64_decode_table[str[i]];
+      if (d < 0 || d >= ENCODE_BASE) goto fail;
+      if (count >= MAX_VALUE / ENCODE_BASE - 1) goto fail;
+      count = count * ENCODE_BASE + d;
+    }
+    if (count < 0 || count >= MAX_VALUE) goto fail;
+    if (count == 0) {
+      if (start > max_num) max_num = start;
+      continue;
+    }
+    if (i + count > len) goto fail;
+    for (; count; --count, ++i) {
+      d = base64_decode_table[str[i]];
+      if (d < 0 || d >= 64) goto fail;
+      for (j = 0; j < 6; ++j, ++start) {
+        if ((d & (1 << j))) {
+          if (start > max_num) max_num = start;
+        }
+      }
+    }
+  }
+
+  if (max_num < 0) goto empty;
+  size = max_num + 1;
+  alloc = (size + 7) / 8;
+  set = (unsigned char *) malloc(alloc);
+  memset(set, 0, alloc);
+  i = 0;
+  while (i < len) {
+    if (!(cnt = str[i++] - '0')) continue;
+    start = 0;
+    for (; cnt; --cnt, ++i) {
+      start = start * ENCODE_BASE + base64_decode_table[str[i]];
+    }
+    if (!(cnt = str[i++] - '0')) {
+      set[start >> 3] |= 1 << (start & 7);
+      continue;
+    }
+    count = 0;
+    for (; cnt; --cnt, ++i) {
+      count = count * ENCODE_BASE + base64_decode_table[str[i]];
+    }
+    if (!count) {
+      set[start >> 3] |= 1 << (start & 7);
+      continue;
+    }
+    for (; count; --count, ++i) {
+      d = base64_decode_table[str[i]];
+      for (j = 0; j < 6; ++j, ++start) {
+        if ((d & (1 << j))) {
+          set[start >> 3] |= 1 << (start & 7);
+        }
+      }
+    }
+  }
+
+  if (ss) {
+    ss->size = size;
+    ss->set = set;
+  }
+  return 0;
+
+empty:
+  return 0;
+
+fail:
+  xfree(set);
+  return -1;
+}
+
+static int
+get_num_len(int val)
+{
+  int cnt = 0;
+  if (val < 0) return 0;
+  if (!val) return 1;
+  while (val) {
+    ++cnt;
+    val /= ENCODE_BASE;
+  }
+  return cnt;
+}
+
+unsigned char *
+bitset_url_encode(bitset_t *ss)
+{
+  int i, j, min_num, max_num, cur_num, size, alloc;
+  unsigned char *str = 0, *s, *set;
+
+  if (!ss || (size = ss->size) <= 0 || !(set = ss->set)) {
+    return xstrdup("");
+  }
+  alloc = (size + 7) / 8;
+  for (i = 0; i < alloc; ++i) {
+    if (set[i]) break;
+  }
+  if (i >= alloc) return xstrdup("");
+  for (j = 0; j < 8; ++j) {
+    if ((set[i] & (1 << j)))
+      break;
+  }
+  min_num = i * 8 + j;
+  for (i = alloc - 1; i >= 0; --i) {
+    if (set[i]) break;
+  }
+  if (i < 0) return xstrdup("");
+  for (j = 7; j >= 0; --j) {
+    if ((set[i] & (1 << j)))
+      break;
+  }
+  max_num = i * 8 + j;
+
+  // count the required size
+  int out_size = 0;
+  cur_num = min_num;
+  while (cur_num <= max_num) {
+    int num2 = cur_num + 1;
+    while (num2 <= max_num && (set[num2 >> 3] & (1 << (num2 & 7)))) {
+      ++num2;
+    }
+    if (num2 <= max_num) {
+      while (1) {
+        int num3 = num2;
+        while (!(set[num3 >> 3] & (1 << (num3 & 7)))) {
+          ++num3;
+        }
+        // series of '0': [num2, num3)
+        // determine if we should terminate the current series
+        int bi1 = (num2 - 1 - cur_num) / 6;
+        int bi2 = (num3 - cur_num) / 6; 
+        if (bi2 - bi1 > 5) {
+          // the gap is too long
+          break;
+        }
+        num2 = num3;
+        while (num2 <= max_num && (set[num2 >> 3] & (1 << (num2 & 7)))) {
+          ++num2;
+        }
+        if (num2 > max_num) {
+          break;
+        }
+      }
+    }
+    // encode interval: [cur_num, num2)
+    out_size += get_num_len(cur_num) + 1; // position bytes
+    if (num2 - cur_num <= 1) {
+      ++out_size;
+    } else {
+      int sb = (num2 - cur_num + 5) / 6;
+      out_size += get_num_len(sb) + 1 + sb;
+    }
+    cur_num = num2;
+    if (cur_num > max_num) break;
+    while (!(set[cur_num >> 3] & (1 << (cur_num & 7)))) {
+      ++cur_num;
+    }
+  }
+
+  // out_size is the required size
+  str = (unsigned char *) xmalloc(out_size + 1);
+  s = str;
+  cur_num = min_num;
+  while (cur_num <= max_num) {
+    int num2 = cur_num + 1;
+    while (num2 <= max_num && (set[num2 >> 3] & (1 << (num2 & 7)))) {
+      ++num2;
+    }
+    if (num2 <= max_num) {
+      while (1) {
+        int num3 = num2;
+        while (!(set[num3 >> 3] & (1 << (num3 & 7)))) {
+          ++num3;
+        }
+        // series of '0': [num2, num3)
+        // determine if we should terminate the current series
+        int bi1 = (num2 - 1 - cur_num) / 6;
+        int bi2 = (num3 - cur_num) / 6; 
+        if (bi2 - bi1 > 5) {
+          // the gap is too long
+          break;
+        }
+        num2 = num3;
+        while (num2 <= max_num && (set[num2 >> 3] & (1 << (num2 & 7)))) {
+          ++num2;
+        }
+        if (num2 > max_num) {
+          break;
+        }
+      }
+    }
+    // encode interval: [cur_num, num2)
+    int cnt = get_num_len(cur_num);
+    *s++ = base64_encode_table[cnt];
+    s += cnt;
+    int val = cur_num;
+    for (int cnt2 = cnt; cnt2; --cnt2) {
+      *--s = base64_encode_table[val % ENCODE_BASE];
+      val /= ENCODE_BASE;
+    }
+    s += cnt;
+    if (num2 - cur_num <= 1) {
+      *s++ = '0';
+    } else {
+      int sb = (num2 - cur_num + 5) / 6;
+      cnt = get_num_len(sb);
+      *s++ = base64_encode_table[cnt];
+      s += cnt;
+      val = sb;
+      for (int cnt2 = cnt; cnt2; --cnt2) {
+        *--s = base64_encode_table[val % ENCODE_BASE];
+        val /= ENCODE_BASE;
+      }
+      s += cnt;
+      val = 0;
+      cnt = 0;
+      for (; cur_num < num2; ++cur_num) {
+        if ((set[cur_num >> 3] & (1 << (cur_num & 7)))) {
+          val |= 1 << cnt;
+        }
+        if (++cnt == 6) {
+          *s++ = base64_encode_table[val];
+          val = 0;
+          cnt = 0;
+        }
+      }
+      if (cnt > 0) {
+        *s++ = base64_encode_table[val];
+      }
+    }
+    cur_num = num2;
+    if (cur_num > max_num) break;
+    while (!(set[cur_num >> 3] & (1 << (cur_num & 7)))) {
+      ++cur_num;
+    }
+  }
+  *s = 0;
+  return str;
+}
+
+int
+bitset_is_equal(
+        bitset_t *s1,
+        bitset_t *s2)
+{
+  int size1 = 0, size2 = 0;
+  if (s1) size1 = s1->size;
+  if (s2) size2 = s2->size;
+  unsigned char *set1 = 0, *set2 = 0;
+  if (s1) set1 = s1->set;
+  if (s2) set2 = s2->set;
+
+  if (size1 < 0 || !set1) size1 = 0;
+  if (size2 < 0 || !set2) size2 = 0;
+  if (!size1 && !size2) return 1;
+  int min_size = size1;
+  if (size2 < min_size) min_size = size2;
+  int i = 0;
+  for (; i < min_size; ++i) {
+    if (((set1[i >> 3] ^ set2[i >> 3]) & (1 << (i & 7))))
+      return 0;
+  }
+  if (size1 > min_size) {
+    for (; i < size1; ++i) {
+      if ((set1[i >> 3] & (1 << (i & 7))))
+        return 0;
+    }
+  } else if (size2 > min_size) {
+    for (; i < size2; ++i) {
+      if ((set2[i >> 3] & (1 << (i & 7))))
+        return 0;
+    }
+  }
+  return 1;
+}
+
+void
+bitset_free(bitset_t *ss)
+{
+  if (ss) {
+    xfree(ss->set); ss->set = 0;
+    ss->size = 0;
+  }
+}
+
+void
+bitset_resize(bitset_t *ss, int new_size)
+{
+  if (!ss || new_size <= 0 || new_size > MAX_VALUE || new_size <= ss->size) return;
+  int new_alloc = (new_size + 7) / 8;
+  unsigned char *new_set = (unsigned char *) malloc(new_alloc);
+  memset(new_set, 0, new_alloc);
+  if (ss->size > 0) {
+    int old_alloc = (ss->size + 7) / 8;
+    memcpy(new_set, ss->set, old_alloc);
+    xfree(ss->set); ss->set = 0;
+  }
+  ss->size = new_size;
+  ss->set = new_set;
+}
diff -ruNp ejudge-2.3.18/bitset.h ejudge/bitset.h
--- ejudge-2.3.18/bitset.h	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/bitset.h	2011-07-11 09:02:04.104281470 +0400
@@ -0,0 +1,107 @@
+/* -*- c -*- */
+/* $Id: bitset.h 6355 2011-06-05 07:45:04Z cher $ */
+
+#ifndef __BITSET_H__
+#define __BITSET_H__
+
+/* Copyright (C) 2011 Alexander Chernov <cher@ejudge.ru> */
+
+/*
+ * 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.
+ */
+
+struct bitset_s
+{
+  int size;
+  unsigned char *set;
+};
+typedef struct bitset_s bitset_t;
+
+#define BITSET_INITIALIZER { 0, 0 }
+
+void bitset_init(bitset_t *ss, int size);
+void bitset_free(bitset_t *ss);
+void bitset_resize(bitset_t *ss, int new_size);
+void bitset_reset(bitset_t *ss, int new_size);
+
+void bitset_clear(bitset_t *ss);
+void bitset_fill(bitset_t *ss);
+
+int
+bitset_url_decode(
+        const unsigned char *str,
+        bitset_t *ss);
+unsigned char *
+bitset_url_encode(bitset_t *ss);
+int
+bitset_is_equal(
+        bitset_t *s1,
+        bitset_t *s2);
+
+static inline __attribute__((always_inline)) int
+bitset_get(bitset_t *ss, int index)
+{
+  return ss->set[index >> 3] & (1 << (index & 7));
+}
+
+static inline __attribute__((always_inline)) int
+bitset_safe_get(bitset_t *ss, int index)
+{
+  if (ss && index >= 0 && index < ss->size) {
+    return ss->set[index >> 3] & (1 << (index & 7));
+  } else {
+    return 0;
+  }
+}
+
+static inline __attribute__((always_inline)) void
+bitset_on(bitset_t *ss, int index)
+{
+  ss->set[index >> 3] |= (1 << (index & 7));
+}
+
+static inline __attribute__((always_inline)) void
+bitset_safe_on(bitset_t *ss, int index)
+{
+  if (ss && index >= 0 && index < ss->size) {
+    ss->set[index >> 3] |= (1 << (index & 7));
+  }
+}
+
+static inline __attribute__((always_inline)) void
+bitset_off(bitset_t *ss, int index)
+{
+  ss->set[index >> 3] &= ~(1 << (index & 7));
+}
+
+static inline __attribute__((always_inline)) void
+bitset_safe_off(bitset_t *ss, int index)
+{
+  if (ss && index >= 0 && index < ss->size) {
+    ss->set[index >> 3] &= ~(1 << (index & 7));
+  }
+}
+
+static inline __attribute__((always_inline)) void
+bitset_toggle(bitset_t *ss, int index)
+{
+  ss->set[index >> 3] ^= (1 << (index & 7));
+}
+
+static inline __attribute__((always_inline)) void
+bitset_safe_toggle(bitset_t *ss, int index)
+{
+  if (ss && index >= 0 && index < ss->size) {
+    ss->set[index >> 3] ^= (1 << (index & 7));
+  }
+}
+
+#endif /* __BITSET_H__ */
diff -ruNp ejudge-2.3.18/ChangeLog ejudge/ChangeLog
--- ejudge-2.3.18/ChangeLog	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/ChangeLog	2011-07-11 09:02:04.508274043 +0400
@@ -1,3 +1,680 @@
+2011-07-04 (r6393) Alexander Chernov  <cher@ejudge.ru>
+
+	* compile.c:
+	style checker output to the standard output is stored in a separate file.
+
+	* new_server_html_2.c:
+	(ns_write_priv_source) style checker output is shown in all cases.
+
+	* serve_2.c:
+	(serve_read_compile_packet) style checker report file is supported.
+
+2011-06-30 (r6387) Alexander Chernov  <cher@ejudge.ru>
+
+	* lang_config_vis.c:
+	(lang_config_generate_compile_cfg) support for "binary" configuration parameter
+	is added.
+
+2011-06-25 (r6381) Alexander Chernov  <cher@ejudge.ru>
+
+	* new-server.h:
+	new commands are added:
+	NEW_SRV_ACTION_DUMP_LANGUAGES, NEW_SRV_ACTION_GET_CONTEST_STATUS,
+	NEW_SRV_ACTION_GET_CONTEST_SCHED, NEW_SRV_ACTION_GET_CONTEST_DURATION,
+	NEW_SRV_ACTION_GET_CONTEST_DURATION;
+	according to a patch from germanilyin@gmail.com.
+
+	* new_server_html.c, new_server_html_4.c, new-server-cmd.c:
+	new commands are added:
+	NEW_SRV_ACTION_DUMP_LANGUAGES, NEW_SRV_ACTION_GET_CONTEST_STATUS,
+	NEW_SRV_ACTION_GET_CONTEST_SCHED, NEW_SRV_ACTION_GET_CONTEST_DURATION,
+	NEW_SRV_ACTION_GET_CONTEST_DURATION;
+	according to a patch from germanilyin@gmail.com.
+
+2011-06-19 (r6377) Alexander Chernov  <cher@ejudge.ru>
+
+	* super_proto.h: progress.
+
+	* userlist_proto.h:
+	'ULS_GET_GROUP_INFO' is added.
+
+	* super_html_4.c, super_html_6.c: progress.
+
+	* userlist-server.c:
+	'ULS_GET_GROUP_INFO' request is implemented.
+
+2011-06-18 (r6370) Alexander Chernov  <cher@ejudge.ru>
+
+	* super_proto.h:
+	SSERV_OP_USER_JUMP_CONTEST_ACTION is added.
+
+	* super_html_4.c, super_html_6.c:
+	jumping to another contest is implemented.
+
+2011-06-18 (r6368) Alexander Chernov  <cher@ejudge.ru>
+
+	* serve_2.c:
+	(serve_read_compile_packet) check for user name is removed.
+
+	* userlist-server.c:
+	userlist updating is improved.
+
+2011-06-12 (r6363) Alexander Chernov  <cher@ejudge.ru>
+
+	* super_proto.h: progress.
+
+	* userlist_proto.h:
+	'ULS_LIST_ALL_USERS_4' is added.
+
+	* super_html_4.c, super_html_6.c: progress.
+
+	* userlist-server.c:
+	'ULS_LIST_ALL_USERS_4' is implemented.
+
+2011-06-11 (r6361) Alexander Chernov  <cher@ejudge.ru>
+
+	* super_html_4.c, super_html_6.c: progress.
+
+2011-06-10 (r6359) Alexander Chernov  <cher@ejudge.ru>
+
+	* super_proto.h:
+	SSERV_OP_USER_SEL_CANCEL_ACTION is added.
+
+	* super-serve.h:
+	"bitset.h" is included;
+	(struct sid_state) 'marked' field is added.
+
+	* userlist_proto.h:
+	ULS_LIST_ALL_USERS_3 command is added.
+
+	* super_html_4.c, super_html_6.c: progress
+
+	* super-serve.c:
+	(sid_state_clear) 'marked' field is freed.
+
+	* userlist-server.c:
+	"bitset.h" is included;
+	(cmd_list_all_users_3) new command is implemented.
+
+2011-06-05 (r6356) Alexander Chernov  <cher@ejudge.ru>
+
+	* files.make:
+	bitset.[ch] are added.
+
+	* super_proto.h: progress...
+
+	* super_html_4.c, super_html_6.c: progress...
+
+2011-06-05 (r6355) Alexander Chernov  <cher@ejudge.ru>
+
+	* bitset.c, bitset.h:
+	initial revision
+
+2011-05-28 (r6353) Alexander Chernov  <cher@ejudge.ru>
+
+	* new-server.h:
+	(ns_header) 'cnts' parameter is added;
+	(ns_separator) new function prototype.
+
+	* new_server_html.c, new_server_html_2.c, new_server_html_5.c:
+	calls of 'ns_header' are updated;
+	calls of 'ns_separator' are added instead of fprintfs.
+
+	* new_server_html_3.c: copyright notice is updated;
+	header and separator templated are changed to use substitutions;
+	(process_template) 'logo_url', 'css_url' parameters are added;
+	  '%O' (logo) and '%Y' (css) substitutions are supported;
+	(ns_header) 'cnts' parameter is added;
+	(ns_separator) new function;
+	(ns_footer) call of 'process_template' is updated;
+	calls of 'ns_header' are updated;
+	calls of 'ns_separator' are added.
+
+2011-05-24 (r6351) Alexander Chernov  <cher@ejudge.ru>
+
+	* super_proto.h:
+	editing of new contest elements <logo_url>, <css_url> is supported.
+
+	* serve-control.c, super_html.c, super_html_2.c, super_html_3.c, super_html_4.c, super-serve.c:
+	editing of new contest elements <logo_url>, <css_url> is supported.
+
+2011-05-24 (r6349) Alexander Chernov  <cher@ejudge.ru>
+
+	* contests.h: copyright notice is updated;
+	(struct contest_desc) new fields 'logo_url', 'css_url' are added.
+
+	* contests.c, contest_tmpl.c:
+	new elements <logo_url>, <css_url> are handled.
+
+2011-05-23 (r6347) Alexander Chernov  <cher@ejudge.ru>
+
+	* misctext.h: copyright notice is updated;
+	(is_valid_email_address) new function prototype.
+
+	* super_html.h:
+	new error code 'S_ERR_INV_EMAIL' is added.
+
+	* ejudge-setup.c, job-server.c, new_server_html_5.c, super_html_2.c, super_html_4.c, super_html_6.c:
+	email address is validated using 'is_valid_email_address' function.
+
+	* misctext.c:
+	(is_valid_email_address) new function.
+
+2011-05-23 (r6345) Alexander Chernov  <cher@ejudge.ru>
+
+	* prepare.c:
+	(set_defaults) '%' is considered as a special substitution character
+	when handling style_checker_cmd.
+
+	* sformat.c:
+	(sformat_message) %Gr substitution is supported.
+
+2011-05-22 (r6343) Alexander Chernov  <cher@ejudge.ru>
+
+	* html.h:
+	(write_xml_team_testing_report) new parameters are added:
+	  'sid', 'self_url', 'extra_args', 'action_vec'.
+
+	* mischtml.h:
+	(html_hyperref_attr) new function prototype.
+
+	* html.c:
+	(html_make_title) new function;
+	(write_xml_team_testing_report) new parameters are added:
+	  'sid', 'self_url', 'extra_args', 'action_vec';
+	full archive storage is supported.
+
+	* html_hyperref.c: copyright notice is updated;
+	(html_hyperref_attr) new function.
+
+	* new_server_html.c:
+	(unpriv_view_test) test visibility settings are supported;
+	(unpriv_view_report) call of 'write_xml_team_testing_report' updated.
+
+	* new_server_html_2.c:
+	(ns_write_priv_report) call of 'write_xml_team_testing_report' updated.
+
+2011-05-22 (r6341) Alexander Chernov  <cher@ejudge.ru>
+
+	* html.h:
+	(do_write_kirov_standings) 'user_id' parameter is added.
+
+	* html.c:
+	(do_write_kirov_standings) 'user_id' parameter is added;
+	  standings freezing in user mode is supported correctly;
+	(write_standings) call of 'do_write_kirov_standings' is updated.
+
+	* new_server_html.c, new_server_html_2.c:
+	calls of 'do_write_kirov_standings' are updated.
+
+2011-05-14 (r6333) Alexander Chernov  <cher@ejudge.ru>
+
+	* meta.make: progress...
+
+	* super_html_6.h, super_proto.h: progress...
+
+	* super_html_6.c: progress...
+
+	* uldb_plugin_xml.c:
+	(set_reg_flags_func) copy command (4) is supported.
+
+	* userlist-server.c:
+	(cmd_edit_registration) copy command (4) is supported.
+
+2011-05-09 (r6326) Alexander Chernov  <cher@ejudge.ru>
+
+	* super_html.h:
+	'S_ERR_TOO_MANY_MEMBERS' error code is added.
+
+	* Makefile.unix.in:
+	generation and installation of default 'ejudge-install.sh' is added.
+
+	* super_html_4.c:
+	'S_ERR_TOO_MANY_MEMBERS' error code is added.
+
+	* super_html_6.c:
+	(super_serve_op_USER_CREATE_MEMBER_ACTION) new function.
+
+2011-05-09 (r6325) Alexander Chernov  <cher@ejudge.ru>
+
+	* lang_config_vis.h: copyright notice is updated;
+	(lang_configure_screen) 'batch_mode' parameter is added.
+
+	* ejudge-configure-compilers.c:
+	(visual_setup) call of 'lang_configure_screen' is updated.
+
+	* ejudge-setup.c:
+	(make_sha1_passwd) new function;
+	(do_identity_menu) 'make_sha1_passwd' is called;
+	(initialize_setting_var) mysql plugin is not enabled by default;
+	workdisk is disabled by default;
+	(do_preview_menu) call of 'lang_configure_screen' is updated;
+	(generate_install_script) warning is added to the generated script;
+	check for user is added to the generated script;
+	(preview_install_script) call of 'lang_configure_screen' is updated;
+	(save_install_script) 'batch_mode', 'output_name' parameters are added;
+	(do_main_menu) call of 'save_install_script' is updated;
+	(main) batch mode (-b) option is supported.
+
+	* lang_config_vis.c:
+	(lang_configure_screen) 'batch_mode' parameter is added.
+
+2011-05-08 (r6323) Alexander Chernov  <cher@ejudge.ru>
+
+	* mischtml.h: copyright notice is updated;
+	(html_input_text_js) new function prototype.
+
+	* compile.c:
+	default log file name is changed to 'ej-compile.log'.
+
+	* ejudge-setup.c:
+	default log file names are changed to 'ej-users.log'
+	and 'ej-super-server.log' for the respective programs.
+
+	* html_input_text.c: copyright notice is updated;
+	(html_input_text_js) new function.
+
+	* job-server.c:
+	default log file name is changed to 'ej-jobs.log'.
+
+	* new-server.c:
+	default log file name is changed to 'ej-contests.log'.
+
+	* super_html.c:
+	some obsoleted code is removed;
+	default ej-run log file name is changed to 'ej-run-messages.log'.
+
+	* super_html_6.c: progress...
+
+	* super-serve.c:
+	default ej-run log file name is changed to 'ej-run-messages.log'.
+
+2011-05-08 (r6316) Alexander Chernov  <cher@ejudge.ru>
+
+	* files.make:
+	'userlist_clnt/get_prev_user_id.c' is added.
+
+	* userlist_clnt.h:
+	(userlist_clnt_get_prev_user_id) new function prototype.
+
+2011-05-08 (r6314) Alexander Chernov  <cher@ejudge.ru>
+
+	* super_proto.h: progress...
+
+	* uldb_plugin.h:
+	(struct uldb_plugin_iface) new member functions: 'get_prev_user_id',
+	  'get_next_user_id'.
+
+	* userlist_proto.h:
+	new commands added: ULS_PREV_USER, ULS_NEXT_USER;
+	(struct userlist_pk_list_users_2) 'user_id' field is added.
+
+	* super_html_4.c, super_html_6.c: progress...
+
+	* uldb_plugin_xml.c:
+	(get_prev_user_id_func) new function;
+	(get_next_user_id_func) new function.
+
+	* userlist.c:
+	more prime numbers are added.
+
+	* userlist-server.c:
+	(cmd_next_user) new function;
+	ULS_PREV_USER, ULS_NEXT_USER requests are handled.
+
+2011-05-04 (r6302) Alexander Chernov  <cher@ejudge.ru>
+
+	* meta.make:
+	metainfo for ss_op_param_USER_CREATE_MANY_ACTION is generated.
+
+	* ej_types.h:
+	'ej_int_opt_m1_t' is added.
+
+	* super_html.h:
+	new error codes are added.
+
+	* super_html_6.h:
+	(struct ss_op_param_USER_CREATE_MANY_ACTION) new structure added.
+
+	* super_html_4.c, super_html_6.c: progress...
+
+2011-05-02 (r6294) Alexander Chernov  <cher@ejudge.ru>
+
+	* configure.ac:
+	check for -m32 and -m64 options is added.
+
+2011-05-01 (r6290) Alexander Chernov  <cher@ejudge.ru>
+
+	* files.make:
+	userlist_clnt/create_user_2.c is added.
+
+	* super_html.h: new error codes are added.
+
+	* uldb_plugin.h:
+	(struct uldb_plugin_iface::new_user) the following new parameters
+	are added:
+	'passwd_method', 'is_privileged', 'is_invisible', 'is_banned',
+	'is_locked', 'show_login', 'show_email', 'read_only',
+	'never_clean';
+	(struct uldb_plugin_iface::register_contest) new parameter 'flags'
+	is added.
+
+	* userlist_clnt.h:
+	(userlist_clnt_create_user_2) new function prototype.
+
+	* userlist_proto.h:
+	new command 'ULS_CREATE_USER_2' is added;
+	(struct userlist_pk_create_user_2) new packet structure.
+
+	* super_html_4.c: new error codes are added.
+
+	* super_html_6.c: progress...
+
+	* uldb_plugin_xml.c:
+	(new_user_func) many parameters are added;
+	(register_contest_func) parameter 'flags' is added.
+
+	* userlist-server.c:
+	(check_pk_create_user_2) new function;
+	(send_registration_email) new function;
+	use of 'new_user' DB method is changed;
+	use of 'register_contest' DB method is changed;
+	(cmd_create_user_2) new function.
+
+2011-04-30 (r6286) Alexander Chernov  <cher@ejudge.ru>
+
+	* Makefile.unix.in:
+	'super_html_6_meta' is added;
+	metainfo generation is moved to a separater 'meta.make' file.
+
+	* meta.make: initial revision.
+
+	* ej_types.h:
+	new typedef for CGI param parsing generator are added.
+
+	* meta_generic.h: copyright notice is updated;
+	(struct meta_methods) new fields 'last_tag', 'size' are added;
+	(meta_destroy_fields) new function prototype.
+
+	* super_html.h: error codes are renamed.
+
+	* super_html_6.h: initial revision
+
+	* meta_generic.c:
+	(meta_destroy_fields) new function.
+
+	* super_html_4.c, super_html_6.c: progress...
+
+2011-04-30 (r6284) Alexander Chernov  <cher@ejudge.ru>
+
+	* Makefile.unix.in:
+	'install_ej_super_server' rule is added;
+	'-rdynamic' options is added for ej-super-server.
+
+	* super_html.h:
+	'super_serve_op_*' function prototypes are removed as they are
+	linked dynamically.
+
+	* super_proto.h:
+	some operations are renamed.
+
+	* userlist_proto.h:
+	new commands are added:
+	ULS_PRIV_SET_REG_PASSWD_PLAIN,
+	ULS_PRIV_SET_REG_PASSWD_SHA1,
+	ULS_PRIV_SET_CNTS_PASSWD_PLAIN,
+	ULS_PRIV_SET_CNTS_PASSWD_SHA1.
+
+	* super_html.c:
+	some operations are renamed.
+
+	* super_html_4.c:
+	(do_http_request) dynamic symbol lookup is implemented instead of
+	static function table.
+
+	* super_html_6.c: progress...
+
+	* userlist-server.c:
+	(cmd_priv_set_passwd_2) new function to handle the new commands.
+
+2011-04-28 (r6273) Alexander Chernov  <cher@ejudge.ru>
+
+	* Makefile.unix.in:
+	rules for generation of the contest-1 archive file are added.
+
+	* ejudge-setup.c:
+	(generate_serve_cfg) 'advanced_layout' is enabled;
+	statement file name is 'statement.xml';
+	checker file name is 'checker';
+	(generate_contest_xml) default access permissions
+	are changed to "enable from all";
+	the uuencoded contest-1 archive is taken from an external file.
+
+2011-04-27 (r6271) Alexander Chernov  <cher@ejudge.ru>
+
+	* Makefile.unix.in:
+	style/sprintf.js is added.
+
+	* super_html.h:
+	(super_serve_op_user_create_one_page) new function prototype;
+	(super_serve_op_user_create_many_page) new function prototype;
+	(super_serve_op_user_create_from_csv_page) new function prototype.
+
+	* super_proto.h:
+	new operations are added:
+	SSERV_OP_USER_CREATE_ONE_ACTION,
+	SSERV_OP_USER_CREATE_MANY_ACTION,
+	SSERV_OP_USER_CREATE_FROM_CSV_PAGE,
+	SSERV_OP_USER_CREATE_FROM_CSV_ACTION.
+
+	* super_html_4.c, super_html_6.c: in progress...
+
+2011-04-25 (r6267) Alexander Chernov  <cher@ejudge.ru>
+
+	* super_html.h:
+	(super_serve_op_user_password_page) new function prototype;
+	(super_serve_op_user_cnts_password_page) new function prototype;
+	(super_serve_op_user_create_reg_page) new function prototype;
+	(super_serve_op_user_edit_reg_page) new function prototype;
+	(super_serve_op_user_delete_reg_page) new function prototype.
+
+	* super_proto.h:
+	new operations are added:
+	SSERV_OP_USER_CREATE_ONE_PAGE,
+	SSERV_OP_USER_CREATE_MANY_PAGE,
+	SSERV_OP_USER_CHANGE_PASSWORD_ACTION,
+	SSERV_OP_USER_CNTS_PASSWORD_PAGE,
+	SSERV_OP_USER_CHANGE_CNTS_PASSWORD_ACTION,
+	SSERV_OP_USER_CREATE_REG_ACTION,
+	SSERV_OP_USER_EDIT_REG_ACTION,
+	SSERV_OP_USER_DELETE_REG_ACTION.
+
+	* super_html_4.c, super_html_6.c: progress...
+
+2011-04-24 (r6265) Alexander Chernov  <cher@ejudge.ru>
+
+	* super_html.h:
+	(ss_cgi_param_int_opt) new function prototype;
+	(super_serve_op_user_detail_page) new function prototype.
+
+	* super_proto.h: new operations are added.
+
+	* super_html.c, super_html_4.c, super_html_6.c: progress...
+
+2011-04-22 (r6263) Alexander Chernov  <cher@ejudge.ru>
+
+	* super_html.h:
+	(super_serve_op_browse_groups) new function prototype;
+	(super_serve_op_set_group_filter) new function prototype.
+
+	* super_proto.h:
+	new operations are added.
+
+	* super-serve.h:
+	(struct sid_state) new fields are added.
+
+	* uldb_plugin.h:
+	(get_brief_list_iterator_2) 'group_id' parameter is added;
+	(get_user_count) 'group_id' parameter is added;
+	(get_group_iterator_2) new method;
+	(get_group_count) new method.
+
+	* userlist_clnt.h:
+	(userlist_clnt_list_users_2) 'group_id' parameter is added;
+	(userlist_clnt_get_count) 'group_id' parameter is added.
+
+	* userlist_proto.h:
+	new requests 'ULS_LIST_ALL_GROUPS_2', 'ULS_GET_GROUP_COUNT'
+	are added;
+	(struct userlist_pk_list_users_2) 'group_id' field is added.
+
+	* super_html.c:
+	"Group editor" link is added to the main page.
+
+	* super_html_4.c, super_html_6.c: in progress...
+
+	* uldb_plugin_xml.c:
+	(get_brief_list_iterator_2_func) 'group_id' parameter is added;
+	(get_user_count_func) 'group_id' parameter is added;
+	(get_group_iterator_2_func) new function;
+	(get_group_count_func) new function.
+
+	* userlist.c:
+	protection against cpu_frequency == 0 is added.
+
+	* userlist-server.c:
+	protection against cpu_frequency == 0 is added;
+	(cmd_list_all_users_2) 'group_id' field is handled;
+	(cmd_get_user_count) 'group_id' field is handled;
+	(cmd_list_all_groups_2) new function;
+	(cmd_get_group_count) new function.
+
+2011-04-21 (r6256) Alexander Chernov  <cher@ejudge.ru>
+
+	* super_html.h:
+	(ss_cgi_param_int) function prototype is added.
+
+	* super_html_4.c:
+	(ss_cgi_param_int) the function is made non-static.
+
+	* super_html_6.c: in progress...
+
+	* userlist-server.c:
+	(cmd_get_user_count) the count is logged.
+
+2011-04-20 (r6255) Alexander Chernov  <cher@ejudge.ru>
+
+	* files.make:
+	'userlist_clnt/get_count.c' is added.
+
+	* super_html.h:
+	(super_serve_op_set_user_filter) new function prototype.
+
+	* uldb_plugin.h:
+	(struct uldb_plugin_iface) new member function 'get_user_count'
+	is added.
+
+	* userlist_clnt.h:
+	(userlist_clnt_get_count) new function prototype.
+
+	* userlist_proto.h:
+	'ULS_GET_USER_COUNT' request code is added;
+	'ULS_COUNT' reply code is added;
+	(struct userlist_pk_count) new reply packet.
+
+	* super_html_4.c: new opcodes are added;
+	(do_http_request) 'op_'-prefixed opcodes are handled.
+
+	* super_html_6.c: ...progress...
+
+	* uldb_plugin_xml.c:
+	(get_user_count_func) new function.
+
+	* userlist-server.c:
+	(cmd_get_user_count) new function;
+	'ULS_GET_USER_COUNT' request is handled.
+
+2011-04-19 (r6251) Alexander Chernov  <cher@ejudge.ru>
+
+	* files.make:
+	userlist_clnt/list_users_2.c is added;
+	super_html_6.c is added.
+
+	* super_html.h: copyright notice is updated;
+	(super_serve_op_browse_users) new function prototype.
+
+	* super_proto.h: new operations are added:
+	SSERV_OP_BROWSE_USERS, SSERV_OP_CHANGE_USER_FILTER,
+	SSERV_OP_USER_FILTER_FIRST_PAGE, SSERV_OP_USER_FILTER_PREV_PAGE,
+	SSERV_OP_USER_FILTER_NEXT_PAGE, SSERV_OP_USER_FILTER_LAST_PAGE.
+
+	* super-serve.h: copyright notice is updated;
+	(struct sid_state) new fields 'user_filter_set', 'user_filter',
+	'user_offset', 'user_count' are added;
+	(struct http_request_info) field 'userlist_clnt' is added.
+
+	* uldb_plugin.h: copyright notice is updated;
+	(struct uldb_plugin_iface) 'get_brief_list_iterator_2' member
+	function is added.
+
+	* userlist_clnt.h: copyright notice is added;
+	(userlist_clnt_list_users_2) new function prototype.
+
+	* userlist_proto.h: copyright notice is updated;
+	'ULS_LIST_ALL_USERS_2' command is added;
+	(struct userlist_pk_list_users_2) new struct is added.
+
+	* super_html.c:
+	(super_html_main_page) "User editor" link is added.
+
+	* super_html_4.c:
+	SSERV_OP_BROWSE_USERS request is handled.
+
+	* super_html_6.c: initial revision.
+
+	* super-serve.c:
+	(sid_state_clear) 'user_filter' is freed;
+	(cmd_http_request) 'userlist_clnt' is added to the request structure.
+
+	* uldb_plugin_xml.c:
+	(get_brief_list_iterator_2_func) new function.
+
+	* userlist-server.c:
+	(check_pk_list_users_2) new function;
+	(cmd_list_all_users_2) new function.
+
+2011-04-15 (r6247) Alexander Chernov  <cher@ejudge.ru>
+
+	* super_html_3.c:
+	(super_html_lang_cmd) 'size_str_to_size_t' is used;
+	(num_suffix) function is removed.
+
+2011-04-15 (r6246) Alexander Chernov  <cher@ejudge.ru>
+
+	* misctext.h:
+	(size_str_to_num) new function prototype;
+	(size_str_to_size_t) new function prototype.
+
+	* misctext.c:
+	<errno.h> is included;
+	(size_str_to_num) new function;
+	(size_str_to_size_t) new function.
+
+	* parsecfg.c:
+	"misctext.h" is included;
+	(num_suffix) function is removed;
+	(copy_param) 'size_str_to_num', 'size_str_to_size_t' are used.
+
+	* prepare_out.c:
+	(num_to_size) function is removed;
+	(size_t_to_size) function is removed;
+	'num_to_size_str' is used instead of 'num_to_size';
+	'size_t_to_size_str' is used instead of 'size_t_to_size'.
+
+	* super_html_3.c:
+	(super_html_global_param) 'size_str_to_size_t' is used;
+	(super_html_prob_param) 'size_str_to_size_t' is used;
+
 2011-04-10 (r6242) Alexander Chernov  <cher@ejudge.ru>
 
 	* super_html.c:
@@ -19667,7 +20344,7 @@
 2005-09-16  Alexander Chernov  <cher@ispras.ru>
 
 	* ejudge-setup.c, super_html_2.c:
-	$Id: ChangeLog 6245 2011-04-10 05:24:07Z cher $ is properly escaped in order not to be substutued by
+	$Id: ChangeLog 6397 2011-07-06 05:01:19Z cher $ is properly escaped in order not to be substutued by
 	  the ejudge CVS.
 
 	* super_html_2.c:
@@ -19679,7 +20356,7 @@
 	  `SSERV_CMD_CNTS_COPY_ACCESS' is handled using `copy_contest_access';
 	(super_html_serve_probe_run) new function to handle
 	  `SSERV_CMD_SERVE_MNG_PROBE_RUN';
-	(super_html_commit_contest) "$Id: ChangeLog 6245 2011-04-10 05:24:07Z cher $" is printed to the header of the
+	(super_html_commit_contest) "$Id: ChangeLog 6397 2011-07-06 05:01:19Z cher $" is printed to the header of the
 	  new contest XML file and serve.cfg file;
 	  the new contest XML file is put under version control;
 	  version control for the serve.cfg is supported, but the new serve.cfg
@@ -32978,4 +33655,4 @@
 
 END
 
-	$Id: ChangeLog 6245 2011-04-10 05:24:07Z cher $
+	$Id: ChangeLog 6397 2011-07-06 05:01:19Z cher $
diff -ruNp ejudge-2.3.18/checkers/ChangeLog ejudge/checkers/ChangeLog
--- ejudge-2.3.18/checkers/ChangeLog	2011-04-10 09:25:17.000000000 +0400
+++ ejudge/checkers/ChangeLog	2011-07-11 09:01:59.337368991 +0400
@@ -1,3 +1,8 @@
+2011-06-30 (r6386) Alexander Chernov  <cher@ejudge.ru>
+
+	* style_archive.c: copyright notice is updated;
+	support for make-style archives is added.
+
 2011-03-27 (r6192) Alexander Chernov  <cher@ejudge.ru>
 
 	* Makefile.unix.in:
diff -ruNp ejudge-2.3.18/checkers/style_archive.c ejudge/checkers/style_archive.c
--- ejudge-2.3.18/checkers/style_archive.c	2011-04-10 09:25:17.000000000 +0400
+++ ejudge/checkers/style_archive.c	2011-07-11 09:01:59.331369100 +0400
@@ -1,7 +1,7 @@
 /* -*- mode: c -*- */
-/* $Id: style_archive.c 6029 2010-11-03 14:16:30Z cher $ */
+/* $Id: style_archive.c 6392 2011-07-04 20:52:14Z cher $ */
 
-/* Copyright (C) 2010 Alexander Chernov <cher@ejudge.ru> */
+/* Copyright (C) 2010-2011 Alexander Chernov <cher@ejudge.ru> */
 
 /*
  * This program is free software; you can redistribute it and/or modify
@@ -50,6 +50,8 @@ static const unsigned char * const DEFAU
 static const unsigned char * const DEFAULT_OUTPUT_PATTERN = "%03d.ans";
 static const unsigned char * const DEFAULT_TESTS_DIR = "tests";
 static const unsigned char * const DEFAULT_WORK_DIR = "/tmp";
+static const unsigned char * const DEFAULT_PROGRAM_DIR = "solution";
+static const unsigned char * const DEFAULT_SCRIPT_NAME = "Makefile";
 
 struct archive_entry
 {
@@ -875,7 +877,7 @@ check_names(struct archive_file *arch)
   return 0;
 }
 
-int
+static int
 check_tar_tests(
         struct archive_file *arch,
         int max_test_count,
@@ -1055,7 +1057,7 @@ create_arch_dir(
   return 0;
 }
 
-int
+static int
 make_report(
         struct archive_file *arch,
         const unsigned char *path,
@@ -1179,6 +1181,158 @@ fail:
   return -1;
 }
 
+static int
+check_make(
+        struct archive_file *arch,
+        const unsigned char *dir_prefix,
+        const unsigned char *script_name)
+{
+  int retcode = 0;
+  int i;
+  unsigned char n1[512];
+
+  // find the main directory entry
+  snprintf(n1, sizeof(n1), "%s/", dir_prefix);
+  if ((i = find_entry(arch, n1)) < 0) {
+    snprintf(n1, sizeof(n1), "%s", dir_prefix);
+    if ((i = find_entry(arch, n1)) < 0) {
+      error("no %s entry in the archive", n1);
+      return -1;
+    }
+  }
+  if (!S_ISDIR(arch->v[i].type)) {
+    error("%s entry is not a directory", n1);
+    return -1;
+  }
+  if ((arch->v[i].type & 0700) != 0700) {
+    error("invalid permissions on %s entry", n1);
+    return -1;
+  }
+  arch->v[i].is_processed = 1;
+
+  // find the build script entry
+  snprintf(n1, sizeof(n1), "%s/%s", dir_prefix, script_name);
+  i = find_entry(arch, n1);
+  if (i < 0) {
+    error("no %s entry in the archive", n1);
+    return -1;
+  }
+  if (i >= 0) {
+    if (!S_ISREG(arch->v[i].type)) {
+      error("%s is not a regular file", n1);
+      return -1;
+    }
+    if ((arch->v[i].type & 0400) != 0400) {
+      error("invalid permissions on %s entry", n1);
+      return -1;
+    }
+    arch->v[i].is_processed = 1;
+  }
+
+  return retcode;
+}
+
+static int
+make_make_report(
+        struct archive_file *arch,
+        const unsigned char *path,
+        const unsigned char *work_dir,
+        const unsigned char *prefix,
+        const unsigned char *program_dir,
+        const unsigned char *script_name,
+        int (*unpack_func)(const unsigned char *path,const unsigned char *dir))
+{
+  unsigned char wd[PATH_MAX];
+  int wd_created = 0;
+  unsigned char td[PATH_MAX];
+  struct stat stb;
+  unsigned char fp[PATH_MAX];
+  char *txt = 0;
+  size_t len = 0;
+  DIR *d = 0;
+  struct dirent *dd;
+
+  if (create_arch_dir(wd, sizeof(wd), work_dir, prefix) < 0)
+    goto fail;
+  wd_created = 1;
+
+  if (unpack_func(path, wd) < 0)
+    goto fail;
+
+  // check, that all files in the working dir have good names
+  if (!(d = opendir(wd))) {
+    error("cannot open directory %s", wd);
+    goto fail;
+  }
+  while ((dd = readdir(d))) {
+    if (!strcmp(dd->d_name, ".")) continue;
+    if (!strcmp(dd->d_name, "..")) continue;
+    if (check_file_name(dd->d_name) < 0) {
+      error("name '%s' is invalid", dd->d_name);
+      goto fail;
+    }
+    if (strcmp(dd->d_name, program_dir) != 0) {
+      error("archive contains invalid entries");
+      goto fail;
+    }
+    snprintf(td, sizeof(td), "%s/%s", wd, dd->d_name);
+    if (stat(td, &stb) < 0) {
+      error("entry %s does not exist", td);
+      goto fail;
+    }
+    if (!S_ISDIR(stb.st_mode)) {
+      error("directory %s is not a directory", td);
+      goto fail;
+    }
+    if (access(td, R_OK | W_OK | X_OK) < 0) {
+      error("directory %s has invalid permissions", td);
+      goto fail;
+    }
+  }
+  closedir(d); d = 0;
+
+  if (!(d = opendir(td))) {
+    error("cannot open directory %s", td);
+    goto fail;
+  }
+  while ((dd = readdir(d))) {
+    if (!strcmp(dd->d_name, ".")) continue;
+    if (!strcmp(dd->d_name, "..")) continue;
+    if (check_file_name(dd->d_name) < 0) {
+      error("name '%s' is invalid", dd->d_name);
+      goto fail;
+    }
+    snprintf(fp, sizeof(fp), "%s/%s", td, dd->d_name);
+    if (lstat(fp, &stb) < 0) {
+      error("entry %s does not exist", td);
+      goto fail;
+    }
+    if (!S_ISREG(stb.st_mode)) {
+      error("directory %s is not a directory", td);
+      goto fail;
+    }
+    if (access(td, R_OK | W_OK) < 0) {
+      error("directory %s has invalid permissions", td);
+      goto fail;
+    }
+    if (read_text_file(fp, dd->d_name, &txt, &len) >= 0) {
+      printf("=== %s ===\n%s\n", dd->d_name, txt);
+      free(txt); txt = 0; len = 0;
+    }
+  }
+  closedir(d); d = 0;
+  
+  remove_directory_recursively(wd, 0);
+  return 0;
+
+fail:
+  if (d) closedir(d);
+  if (wd_created) {
+    remove_directory_recursively(wd, 0);
+  }
+  return -1;
+}
+
 int
 main(int argc, char **argv)
 {
@@ -1189,13 +1343,16 @@ main(int argc, char **argv)
   int max_test_count = -1;
   long long tmp;
   int max_file_count = -1;
-  int tests_mode = 0;
+  int tests_mode = -1;
+  int make_mode = 0;
   int mime_type;
   struct archive_file arch;
   const unsigned char *if_patt = 0;
   const unsigned char *of_patt = 0;
   const unsigned char *tests_dir = 0;
   const unsigned char *work_dir = 0;
+  const unsigned char *program_dir = 0;
+  const unsigned char *script_name = 0;
   int (*unpack_func)(const unsigned char *path,const unsigned char *dir) = 0;
   const unsigned char *env;
   int no_readme_mode = 0;
@@ -1247,9 +1404,18 @@ main(int argc, char **argv)
   if ((env = getenv("EJ_WORK_DIR"))) {
     work_dir = env;
   }
+  if ((env = getenv("EJ_PROGRAM_DIR"))) {
+    program_dir = env;
+  }
+  if ((env = getenv("EJ_SCRIPT_NAME"))) {
+    script_name = env;
+  }
   if ((env = getenv("EJ_TESTS_MODE"))) {
     tests_mode = 1;
   }
+  if ((env = getenv("EJ_MAKE_MODE"))) {
+    make_mode = 1;
+  }
   if ((env = getenv("EJ_NO_README"))) {
     no_readme_mode = 1;
   }
@@ -1296,6 +1462,10 @@ main(int argc, char **argv)
       // test archive check mode
       tests_mode = 1;
       ++i;
+    } else if (!strcmp(argv[i], "-m")) {
+      // make archive check mode
+      make_mode = 1;
+      ++i;
     } else if (!strcmp(argv[i], "-r")) {
       // no README mode
       no_readme_mode = 1;
@@ -1338,6 +1508,18 @@ main(int argc, char **argv)
         die("argument expected for -w option");
       work_dir = argv[i + 1];
       i += 2;
+    } else if (!strcmp(argv[i], "-p")) {
+      // program directory
+      if (i + 1 >= argc)
+        die("argument expected for -p option");
+      program_dir = argv[i + 1];
+      i += 2;
+    } else if (!strcmp(argv[i], "-s")) {
+      // script name
+      if (i + 1 >= argc)
+        die("argument expected for -s option");
+      script_name = argv[i + 1];
+      i += 2;
     } else {
       die("invalid option '%s'", argv[i]);
     }
@@ -1350,6 +1532,8 @@ main(int argc, char **argv)
     die("invalid parameters after file name");
   }
 
+  if (tests_mode < 0 && make_mode <= 0) tests_mode = 1;
+  if (tests_mode < 0) tests_mode = 0;
   if (max_archive_size <= 0) max_archive_size = DEFAULT_MAX_ARCHIVE_SIZE;
   if (max_file_size <= 0) max_file_size = DEFAULT_MAX_FILE_SIZE;
   if (max_file_count <= 0) max_file_count = DEFAULT_MAX_FILE_COUNT;
@@ -1358,6 +1542,8 @@ main(int argc, char **argv)
   if (!of_patt) of_patt = DEFAULT_OUTPUT_PATTERN;
   if (!tests_dir) tests_dir = DEFAULT_TESTS_DIR;
   if (!work_dir) work_dir = DEFAULT_WORK_DIR;
+  if (!program_dir) program_dir = DEFAULT_PROGRAM_DIR;
+  if (!script_name) script_name = DEFAULT_SCRIPT_NAME;
 
   if (access(archive_path, R_OK) < 0)
     die("file %s does not exist or is not readable");
@@ -1389,13 +1575,20 @@ main(int argc, char **argv)
   if (check_sizes(&arch, max_file_count, max_file_size, max_archive_size) < 0)
     return 1;
   if (check_names(&arch) < 0) return 1;
-  if (check_tar_tests(&arch, max_test_count, tests_dir, if_patt, of_patt,
-                      no_readme_mode, ignore_dot_files) < 0)
-    return 1;
 
-  if (make_report(&arch, archive_path, work_dir, "stylearch", tests_dir,
-                  if_patt, of_patt, unpack_func, ignore_dot_files) < 0)
-    return 1;
+  if (tests_mode) {
+    if (check_tar_tests(&arch, max_test_count, tests_dir, if_patt, of_patt,
+                        no_readme_mode, ignore_dot_files) < 0)
+      return 1;
+    if (make_report(&arch, archive_path, work_dir, "stylearch", tests_dir,
+                    if_patt, of_patt, unpack_func, ignore_dot_files) < 0)
+      return 1;
+  } else if (make_mode) {
+    if (check_make(&arch, program_dir, script_name) < 0)
+      return 1;
+    if (make_make_report(&arch, archive_path, work_dir, "makearch", program_dir, script_name, unpack_func) < 0)
+      return 1;
+  }
 
   return 0;
 }
@@ -1403,6 +1596,5 @@ main(int argc, char **argv)
 /*
  * Local variables:
  *  compile-command: "make"
- *  c-font-lock-extra-types: ("\\sw+_t" "FILE" "va_list" "fd_set" "DIR")
  * End:
  */
diff -ruNp ejudge-2.3.18/compile.c ejudge/compile.c
--- ejudge-2.3.18/compile.c	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/compile.c	2011-07-11 09:02:04.582272683 +0400
@@ -1,5 +1,5 @@
 /* -*- c -*- */
-/* $Id: compile.c 6214 2011-04-01 20:03:14Z cher $ */
+/* $Id: compile.c 6393 2011-07-04 20:54:33Z cher $ */
 
 /* Copyright (C) 2000-2011 Alexander Chernov <cher@ejudge.ru> */
 
@@ -78,6 +78,7 @@ check_style_only(
   size_t reply_bin_size = 0;
   unsigned char msgbuf[1024] = { 0 };
   path_t log_path;
+  path_t txt_path;
   path_t work_src_path;
   path_t work_log_path;
   path_t work_txt_path;
@@ -91,6 +92,7 @@ check_style_only(
   // working directory: ${global->compile_work_dir}
 
   snprintf(log_path, sizeof(log_path), "%s/%s", report_dir, run_name);
+  snprintf(txt_path, sizeof(txt_path), "%s/%s.txt", report_dir, run_name);
   if (req->src_sfx) src_sfx = req->src_sfx;
   snprintf(work_src_path, sizeof(work_src_path), "%s/%s%s",
            global->compile_work_dir, run_name, src_sfx);
@@ -171,11 +173,12 @@ check_style_only(
     rpl->status = RUN_STYLE_ERR;
     get_current_time(&rpl->ts3, &rpl->ts3_us);
     generic_copy_file(0, 0, work_log_path, "", 0, 0, log_path, "");
+    generic_copy_file(0, 0, work_txt_path, "", 0, 0, txt_path, "");
   } else {
     // success
     rpl->status = RUN_OK;
     get_current_time(&rpl->ts3, &rpl->ts3_us);
-    generic_copy_file(0, 0, work_txt_path, "", 0, 0, log_path, "");
+    generic_copy_file(0, 0, work_txt_path, "", 0, 0, txt_path, "");
   }
 
   if (compile_reply_packet_write(rpl, &reply_bin_size, &reply_bin) < 0)
@@ -213,9 +216,11 @@ do_loop(void)
   path_t src_path;
   path_t exe_path;
   path_t log_path;
+  path_t txt_path;
 
   path_t exe_out;
   path_t log_out;
+  path_t txt_out;
   path_t report_dir, status_dir;
 
   path_t  pkt_name, run_name;
@@ -323,6 +328,7 @@ do_loop(void)
              "%s/%06d/status", global->compile_dir, rpl.contest_id);
     snprintf(run_name, sizeof(run_name), "%06d", rpl.run_id);
     pathmake(log_out, report_dir, "/", run_name, NULL);
+    snprintf(txt_out, sizeof(txt_out), "%s/%s.txt", report_dir, run_name);
 
     make_all_dir(status_dir, 0777);
     make_dir(report_dir, 0777);
@@ -355,6 +361,7 @@ do_loop(void)
     pathmake(src_path, global->compile_work_dir, "/", src_name, NULL);
     pathmake(exe_path, global->compile_work_dir, "/", exe_name, NULL);
     pathmake(log_path, global->compile_work_dir, "/", "log", NULL);
+    pathmake(txt_path, global->compile_work_dir, "/", "txt", NULL);
     /* the resulting executable file */
     pathmake(exe_out, report_dir, "/", exe_name, NULL);
 
@@ -391,9 +398,9 @@ do_loop(void)
         task_SetPathAsArg0(tsk);
         task_SetWorkingDir(tsk, global->compile_work_dir);
         task_EnableProcessGroup(tsk);
-        task_SetRedir(tsk, 1, TSR_FILE, log_path, TSK_REWRITE, 0777);
         task_SetRedir(tsk, 0, TSR_FILE, "/dev/null", TSK_WRITE);
-        task_SetRedir(tsk, 2, TSR_DUP, 1);
+        task_SetRedir(tsk, 1, TSR_FILE, txt_path, TSK_REWRITE, 0777);
+        task_SetRedir(tsk, 2, TSR_FILE, log_path, TSK_REWRITE, 0777);
         if (req->sc_env_num > 0) {
           for (i = 0; i < req->sc_env_num; i++)
             task_PutEnv(tsk, req->sc_env_vars[i]);
@@ -536,6 +543,9 @@ do_loop(void)
       } else {
         r = generic_copy_file(0, 0, exe_path, "", 0, 0, exe_out, "");
       }
+      if (generic_file_size(0, txt_path, "") > 0) {
+        generic_copy_file(0, 0, txt_path, "", 0, 0, txt_out, "");
+      }
       if (r >= 0 && generic_write_file(rpl_pkt, rpl_size, SAFE,
                                        status_dir, run_name, "") >= 0)
         break;
@@ -888,7 +898,7 @@ main(int argc, char *argv[])
 #if HAVE_SETSID - 0
   if (daemon_mode) {
     // FIXME: make log file tunable?
-    snprintf(log_path, sizeof(log_path), "%s/compile.log",
+    snprintf(log_path, sizeof(log_path), "%s/ej-compile.log",
              serve_state.global->var_dir);
 
     // daemonize itself
diff -ruNp ejudge-2.3.18/configure ejudge/configure
--- ejudge-2.3.18/configure	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/configure	2011-07-11 09:02:04.295277960 +0400
@@ -1,5 +1,5 @@
 #! /bin/sh
-# From configure.ac Revision: 6189 .
+# From configure.ac Revision: 6388 .
 # Guess values for system-dependent variables and create Makefiles.
 # Generated by GNU Autoconf 2.66 for ejudge 2.3.
 #
@@ -599,6 +599,8 @@ ac_cv_lang_config_dir
 ac_cv_ejudge_server_bin_path_m
 ac_cv_libcap_link
 ac_cv_festival_home
+ac_cv_need_64
+ac_cv_need_32
 ac_cv_meta_cc
 ac_cv_has_lib_libzip
 ac_cv_has_lib_intl
@@ -6992,6 +6994,86 @@ rm -f core *.core core.conftest.* gmon.o
 fi
 
 
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for -m32 support" >&5
+$as_echo_n "checking for -m32 support... " >&6; }
+saved_CC="${CC}"
+CC="${CC} -m32 -fPIC -DPIC"
+if test "$cross_compiling" = yes; then :
+  { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot run test program while cross compiling
+See \`config.log' for more details" "$LINENO" 5; }
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+#include <string.h>
+#include <stdio.h>
+int main(void)
+{
+  printf("");
+  return 0;
+}
+
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ac_cv_need_32=1
+
+else
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+CC="${saved_CC}"
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for -m64 support" >&5
+$as_echo_n "checking for -m64 support... " >&6; }
+saved_CC="${CC}"
+CC="${CC} -m64 -fPIC -DPIC"
+if test "$cross_compiling" = yes; then :
+  { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot run test program while cross compiling
+See \`config.log' for more details" "$LINENO" 5; }
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+#include <string.h>
+#include <stdio.h>
+int main(void)
+{
+  printf("");
+  return 0;
+}
+
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ac_cv_need_64=1
+
+else
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+CC="${saved_CC}"
+
 
 if [ x"${ac_cv_nls}" = xno ]
 then
@@ -7360,6 +7442,8 @@ fi
 
 
 
+
+
 if [ x"${ac_cv_has_libzip}" = x1 -a x"${ac_cv_has_lib_libzip}" != x ]
 then
   $as_echo "#define CONF_HAS_LIBZIP 1" >>confdefs.h
diff -ruNp ejudge-2.3.18/configure.ac ejudge/configure.ac
--- ejudge-2.3.18/configure.ac	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/configure.ac	2011-07-11 09:02:04.034282758 +0400
@@ -1,9 +1,9 @@
-dnl $Id: configure.ac 6189 2011-03-27 15:59:42Z cher $
+dnl $Id: configure.ac 6388 2011-06-30 11:49:25Z cher $
 dnl Copyright (C) 2004-2011 Alexander Chernov <cher@ejudge.ru>
 
 AC_INIT(ejudge,2.3)
 AC_PREREQ(2.59)
-AC_REVISION($Revision: 6189 $)
+AC_REVISION($Revision: 6388 $)
 
 AC_PREFIX_DEFAULT([$HOME/inst-ejudge])
 AC_PROG_INSTALL
@@ -747,6 +747,44 @@ AC_MSG_RESULT([no])
 ac_cv_has___int64=""
 ])
 
+AC_MSG_CHECKING([for -m32 support])
+saved_CC="${CC}"
+CC="${CC} -m32 -fPIC -DPIC"
+AC_RUN_IFELSE([[
+#include <string.h>
+#include <stdio.h>
+int main(void)
+{
+  printf("");
+  return 0;
+}
+]],[
+AC_MSG_RESULT([yes])
+ac_cv_need_32=1
+],[
+AC_MSG_RESULT([no])
+])
+CC="${saved_CC}"
+
+AC_MSG_CHECKING([for -m64 support])
+saved_CC="${CC}"
+CC="${CC} -m64 -fPIC -DPIC"
+AC_RUN_IFELSE([[
+#include <string.h>
+#include <stdio.h>
+int main(void)
+{
+  printf("");
+  return 0;
+}
+]],[
+AC_MSG_RESULT([yes])
+ac_cv_need_64=1
+],[
+AC_MSG_RESULT([no])
+])
+CC="${saved_CC}"
+
 dnl localization stuff
 
 if [[ x"${ac_cv_nls}" = xno ]]
@@ -970,6 +1008,8 @@ AC_SUBST(ac_cv_has_lib_libiconv)
 AC_SUBST(ac_cv_has_lib_intl)
 AC_SUBST(ac_cv_has_lib_libzip)
 AC_SUBST(ac_cv_meta_cc)
+AC_SUBST(ac_cv_need_32)
+AC_SUBST(ac_cv_need_64)
 
 AC_SUBST(ac_cv_festival_home)
 AC_SUBST(ac_cv_libcap_link)
diff -ruNp ejudge-2.3.18/contest-1/contest-1.c ejudge/contest-1/contest-1.c
--- ejudge-2.3.18/contest-1/contest-1.c	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/contest-1/contest-1.c	2011-07-11 09:02:01.450330222 +0400
@@ -0,0 +1,85 @@
+"begin-base64 664 contest-1.tar.gz\n"
+"H4sIAFQ/FU4AA+1dW2/bRhb2g1/CXzHQJoWU2DTvrGNJQS7ewthttrBT9KEt\n"
+"DIqkZSYUJZCUY7cokMToBUi6xe5LgQWKPu6rm9Zbp46dv0D9oz0zpCg5liyn\n"
+"UcZOdL7EIuc+nOE3c2bmzLAVNmu+24jmpt4cJIBp6unV0I5cM0zJsq4bsqGY\n"
+"CtjLsqpLU0R/g3nK0Y5iKyRkyl53w5P8jXJ/S9Hq1v/1N/cGvEL9q5Km0PrX\n"
+"FQXrnwf66j92o/iNtAOvXv+KAc5Y/xxwrP4lSRUdKx5nGrSCDU07of71KRl+\n"
+"NFNRDEWC+lepdyKNMxPDMOH1P6sJmnDWmUCcGQby3wqicabB+G+cxH9o86GD\n"
+"UBVJ1Qyd8d+Ukf88ICH5JxoD+K+dKf+B+JT/hon854H3kf8TjQH8l89S/tcN\n"
+"g/FfV5H/PCALsoBNwORiYP/Pnf9a3/jfRP5zBA7/JxsD+K+cKf+ltP/XDOQ/\n"
+"DyiCivyfYAzgv/5G+H/S+F9WgP+mCdw3Zbb+C+N/BfnPA7KiyoKmzc9jKzCR\n"
+"GDj+P8v5P93A+T+OUJD3E42B/T93/mu9/t9Q0vU/lP+5QDdVLsWMOJ8YOP4/\n"
+"0/X/bP0P9X+4QMf+f6LRx38gQuw23CAWNxv+ONMYwX9Z18ys/zcVQ1aB/5pi\n"
+"6Mh/Hihfg8omG24Yec2gUpBFqUDcwG46XlCvFNrx2uz7BXKtKpSzF0UghLQs\n"
+"+55Vd0mFFMK26N5tO3VXjKxGy3dX7WZA+5EC9ec51Mt1dh9vtVgAKO3AsUKn\n"
+"UAXbcv7KEd8K6m2ItAJRri5/zJzBQ+zFvltNfkx2kt+Snc63yQ65Xp5LbVMf\n"
+"jhvZodeKIfs0l9XkJ/DTedh5BEEOWKAHcH+QHCbPSfIiOQTDYfJHskuSp/Dv\n"
+"kPogye9Z/P/sPIKgPxAwPKXRfJPsJvudx52vCSS913mY7Cc7MwRig+DPwe2g\n"
+"86TzXbKX7Aqz0HlJEkn2MtdfIOr9rithjqIwJG+dx8mzvrxBzsAmz9sBeH6R\n"
+"7NJnZwGfwe8ujWUbsvC8s00634PNXi+PkGVRKM+1WHEIyX+7GR/wmJAmYemw\n"
+"8oG4kqcsf50HaSGJJPmZ3f/CCuIJyytNHJ7qOcsilA5h2XsAPn7rFRMr7r30\n"
+"qah5V6DlkvzKEmTJb7PfR/Cw2zRDLKltlniaRJ6LPZqrvLZ2h4VPH5m9E3NH\n"
+"Xgpqkb9ozOhuspc1yrxnxtQEZi9oteOqLCjlufS269Bsx9Solueyuyy5vghy\n"
+"A0QOGUpJUz2/nWx/+9/027TExr4HgLX3I/X/DUmVQP6T2Pyvrumo/88DA+vf\n"
+"Wm1txevNQBVbW2NIY+T6n26m+z8kEzp+Wv+GhOt/fGBBp+wFcZE1dMVSSai9\n"
+"bNEKqdEiV0itdH7bMcSfwxD+37U2LHs1ikV687ppjJL/JQXaf0mHYYAuKwod\n"
+"/xu6hPznAq/RaoYxodUsek3x8oIgtNo137PJmhdYPrF9K4qItaoJX4J0kzlR\n"
+"aQouG02Q8BuWFxRXYmgk6sQK69Gnn5fA45dMMgJr12rcad5zA+8LN4Rw0LgE\n"
+"7v2XHYrU7kZ7bc0NXWfZtZzMaom2QanfzHZlKwI5TvSCUqm0wNKAxolYM6QG\n"
+"GadGeGVDN3Ljla0gtjaLmSewdZv+UrTi1QNvzbMtaNHWLD9ye+73m6Fzc90K\n"
+"o6KqzhBF1/uc1j0Y08Cox009SDMg0JeyBONwK3ta5jdwN2P2XJA0oY3rEoyH\n"
+"6m4otiCkC4YieIo2LD+L/nig2mkCZeUAYqjI2mc/yFro1MNXxLZie50UFzdt\n"
+"l4nBZLOUZzML7G56cVHuhhDo31fYwE8YhrT/ddMU15pjavBGzf/KqsL0v3RJ\n"
+"NwyZ7v/UZZz/5YO0RVi6fWfxg8Xlyxo0pbUZO2snlhev3yLFyzOXS6yFzWxt\n"
+"aJ9YW5OZP1leurOYeeuGXLx9C1uStwJD+O/YNnQ+Y1oFGsl/Xe7qf6q0LQAL\n"
+"FeU/PoD6r4dWg0TtxoKwASVBmT4DJL9K/GZQB9FiQai5cKXMBkHMoZJGKjNk\n"
+"pkzmgBBZu8CM90OQmcDVBlc3cERsDs4nhvX/rTPmv4L85wLk/2Rj2PzvekuE\n"
+"v/GkMWr+V9bkfP5XNU3Gf9T/5IPyNahmwfeimBQvWiWQ7dci2wrWiit3bi3d\n"
+"niGFS04BKJx5qA31wGYhyEXaAFysEZEUPgsKC8K1c7zyhaAYwv+1M+//Uf+D\n"
+"C7D/n2wM6//d0BdbY9ICG6n/aXb5r9KlH8Z/Hef/uMBebzZa0PNDv15mPXoV\n"
+"CJtZ1o5YHunhF5DP7wZO1P8Yj/rHaP4bRjb/rxmKSvU/dVPH89+4oKv/EVr3\n"
+"V4/pgByxTPmfzvyfda4R48IQ/sdnLf9reP4LF6D8P9kYNv9/5Ypot8Y5/3cS\n"
+"/3P5X1FM02Dr/xrO/3HBX7zA9tuOS8peM2KaVlWhHVFlrsBqpEpPJIqdBYF2\n"
+"/kzXq8Q0wXpqV0B9LyDVKrH67lP7Zjsm5XKvVQjduB0GRFpARaPzgpP1P20e\n"
+"+p+Sxs5/SfU/ddlM9T9R/ueC4/qf/TbwPvjiim0FgRsO0wytvYJmKInXw+b9\n"
+"iORqiT1V0TQREnU1RFNzn8LnQHVPOnyJmP4k1ZLM/NQGWZ6oMTm5mo/D+n/7\n"
+"7liozzCC/7KkyN3zH3TNYON/zcT5Py44nf73h0BlVABHBXDEu4Yh7X/jiwga\n"
+"vIYrRnbjtdMYOf/L9v+z8Z+pa2z/n4b6n3xQdLyo5VtbpHiFFOmMTim7lEoC\n"
+"bYJ9L3BLeEL4O4uh8p8t2uNKY+T8j5Gf/6MoJp3/1UwD53+5oDf/E8UOCIDr\n"
+"VaE31UPlu5ene8CQ6n8VLjmXnMIMeQ/s30vlDiaJMJfPAnDpCSQ48XNeMYz/\n"
+"67a4Pq5DwEbyn37/Izv/QVW1dP5XQv7zAOU5uXqVLP2DFEsCM1VI3Y1v0nN8\n"
+"gjgi1Wol5TUR6SIR/DasFlv7gVs6ZopQOniLMYT/tVaL4/pPfv6XYeiKmup/\n"
+"4/wvFxxf/6EiwEuLPXQlGFd73kkM4X/Yrm2JYW08aTD+n3T+Dwj7Xf5rKtv/\n"
+"rZp4/j8XWGl3H4lM55NpfvUZW22QACwxbq56lNXsBrn8LmGY/L/WDOPQCsZy\n"
+"CMTI/v/Y+Q+GJiH/uQDPf5hsDJP/+c7/Gfn6LwwA2Pwf9v98cMr5v74RQN8E\n"
+"oO/A/yFTgD7OAb4NGML/e+2GF4rwO440Rsn/smq8dP6nbqo4/88F0vTCtDot\n"
+"TFWmpekP4PrXaR1syJQ1NTNFpmpTwpQyrUxXpzVm129K3f7W50qmrmRhroJd\n"
+"ZRppfv4xhP8btcAWN8Y5/j+h/1dk9v0fw9Tp8f/p/B+e/8sHHzadtu8Sqyas\n"
+"tGtMz6tYEm55jXwfiBV1lZEEOldwE96Ppu+KVB3r717gFtPdYgOse+OEruMn\n"
+"dE8Ic7VLwmLgEEiSXdNMYHNxBhim/2NHos1r/U+V1Jz/msTOf1FQ/5sP0r0e\n"
+"qUIgyPY9dc+b9A5k/wv9up6sfYiYruennzNtz5JwAfxcSLeGMr1FVRE/olqL\n"
+"xeNtAowDUq+103k93nAUvpS+6htVXECNxdfE0PPfwqbfrItwef00Ro//1e7+\n"
+"b02Wmf6fgvs/+eASmb08S9LaprfC1VniBV7sWb73hUVfhyKdCiiJqW4AuNKl\n"
+"/9Wg3ai5YfF6aeaI+QaYbxIvIteBoDdm0l2gxZtgG/gzZN3yYxG1Bc4VhvB/\n"
+"y6pZkWeLtXHsAR/Ff0mXcv1fVWf7v2T8/hcfLN3+6OM7pFAglpDf1vpk94+W\n"
+"l27fITaS9h1Fzv8b4/7qUw+M7yO//6TrwHpV0uj8v0zVgPD7TxzQV//p91/f\n"
+"QBqvXv+KYeD3v7jgWP3DcFx0rHicadAK1k6c/5e7+78VKf3+m4HnP/CBnEGQ\n"
+"FYbuFW7mUxy76VnkNvNHrvSCAsPbgYH85/39b7mP/7qefv8b+c8F8/PQ70LJ\n"
+"I18nEwP4L/Pu/0Hey/f/yWz8rxp4/iMfyIIiqIIm6NgATCQG8F/hzn+65+dl\n"
+"/uP5L1wgS5IAgy5BhT8N/mY1bAgmCQP7f97yv5TzX9WVTP5H/R8ukLHjn2gM\n"
+"7P+581/r8V+VU/7j+h8XzM8b2ABMMPr4TxW93IYbxOJmY0xffkkxgv+yobH9\n"
+"P6qqmQY9C0SSNQXP/+GD8jWobLLhhpHXDCoFWZQKxA3spuMF9UqhHa/Nvl8g\n"
+"16pCOXtR6Aa/lmXfs+ouqZBC2Bbdu22n7oqR1Wj57qpNzw2J4gL15znUyw12\n"
+"H2+1WAAo7cCxQqdQBdty/soR3wrqbYi0AlGuLn/MnMFD7MW+W01+THaS35Kd\n"
+"zrfJDrlRnkttUx+OG9mhx444pLmsJj+Bn87DziMIcsACPYD7g+QweU6SF8kh\n"
+"GA6TP5JdkjyFf4fUB0l+z+L/V+cRBP0h9fgw2U92wfoQ/O1AsN1kv/OExkRj\n"
+"7zwhnW+Y1ePO1wQytgf+wSgKyX8gsv+xcLtdh/3k8BRxJnsEbiFnz8H+oPOk\n"
+"8x0YZpWMJiR3/gWC72fOXVdI998sB7+eKima/YdZqoeQxUf9UYNXlvNdWmR7\n"
+"ye9Ek44+4c+niJ6V6dNkF4r/O7jbzUu284DGCT53oBpYrdDLAZTmTl+dQCqD\n"
+"K7LzOHnWV5EQBGzyiqSxvoDEvmV52kme0ZzRWLahUJ93tknne1rOL1VZea6V\n"
+"vU1zR14napG/oszobrLXPMq8Z8Zqtu21zD6WUpWJQlSiledSY9ex2Y6Zq1Se\n"
+"y26zNPtiyQ2QAuQq5dyb/Ihif/uf63+NOY1XX/9XdUPB9X8eGFj/Nb7n/yk9\n"
+"+V8BGSA9/w/lfy54hfP/6NHs0gzZZFuA7697vkuK+VGAdBfwZolUKkQupccm\n"
+"kysV6vfYqYAR7gZGIBAIBAKBQCAQCAQCgUAgEAgEAoFAIBAIBAKBQCAQCAQC\n"
+"gUAg/iT+D2azPlgA8AAA\n"
+"====\n"
Binary files ejudge-2.3.18/contest-1/contest-1.tar.gz and ejudge/contest-1/contest-1.tar.gz differ
diff -ruNp ejudge-2.3.18/contest-1/make-c.sh ejudge/contest-1/make-c.sh
--- ejudge-2.3.18/contest-1/make-c.sh	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/contest-1/make-c.sh	2011-07-11 09:02:01.448330258 +0400
@@ -0,0 +1,35 @@
+#! /bin/sh
+# $Id: make-c.sh 6275 2011-04-28 03:59:21Z cher $
+
+filename="$1"
+infile="$2"
+outfile="$3"
+
+if [ x"${filename}" = x"" ]
+then
+    echo "filename parameter is not specified" >&2
+    exit 1
+fi
+if [ x"${infile}" = x"" ]
+then
+    echo "infile parameter is not specified" >&2
+    exit 1
+fi
+if [ x"${outfile}" = x"" ]
+then
+    echo "outfile parameter is not specified" >&2
+    exit 1
+fi
+
+if [ ! -f "${infile}" ]
+then
+    echo "${infile} does not exist" >&2
+    exit 1
+fi
+
+if uuencode --help > /dev/null
+then
+    uuencode -m contest-1.tar.gz < contest-1/contest-1.tar.gz | sed 's/$/\\n"/g' | sed 's/^/"/g' > contest-1/contest-1.c
+else
+    touch contest-1/contest-1.c
+fi
diff -ruNp ejudge-2.3.18/contest-1/problems/A/solutions/a_bcc.c ejudge/contest-1/problems/A/solutions/a_bcc.c
--- ejudge-2.3.18/contest-1/problems/A/solutions/a_bcc.c	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/contest-1/problems/A/solutions/a_bcc.c	2011-07-11 09:02:00.713343749 +0400
@@ -0,0 +1,10 @@
+#include <stdio.h>
+
+int main(void)
+{
+  long a, b;
+
+  scanf("%ld%ld", &a, &b);
+  printf("%ld\n", a + b);
+  return 0;
+}
diff -ruNp ejudge-2.3.18/contest-1/problems/A/solutions/a_bpp.cpp ejudge/contest-1/problems/A/solutions/a_bpp.cpp
--- ejudge-2.3.18/contest-1/problems/A/solutions/a_bpp.cpp	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/contest-1/problems/A/solutions/a_bpp.cpp	2011-07-11 09:02:00.709343823 +0400
@@ -0,0 +1,9 @@
+#include <iostream.h>
+int main()
+{
+  long a, b;
+  cin >> a;
+  cin >> b;
+  cout << a + b;
+  return 0;
+}
diff -ruNp ejudge-2.3.18/contest-1/problems/A/solutions/a_dcc.pas ejudge/contest-1/problems/A/solutions/a_dcc.pas
--- ejudge-2.3.18/contest-1/problems/A/solutions/a_dcc.pas	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/contest-1/problems/A/solutions/a_dcc.pas	2011-07-11 09:02:00.728343475 +0400
@@ -0,0 +1,8 @@
+program sum;
+var a, b, c : longint;
+begin
+   readln(a);
+   readln(b);
+   c := a + b;
+   writeln(c);
+end.
diff -ruNp ejudge-2.3.18/contest-1/problems/A/solutions/a_fpc.pas ejudge/contest-1/problems/A/solutions/a_fpc.pas
--- ejudge-2.3.18/contest-1/problems/A/solutions/a_fpc.pas	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/contest-1/problems/A/solutions/a_fpc.pas	2011-07-11 09:02:00.685344264 +0400
@@ -0,0 +1,8 @@
+program sum;
+var a, b, c : longint;
+begin
+   readln(a);
+   readln(b);
+   c := a + b;
+   writeln(c);
+end.
diff -ruNp ejudge-2.3.18/contest-1/problems/A/solutions/a_g77.for ejudge/contest-1/problems/A/solutions/a_g77.for
--- ejudge-2.3.18/contest-1/problems/A/solutions/a_g77.for	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/contest-1/problems/A/solutions/a_g77.for	2011-07-11 09:02:00.681344336 +0400
@@ -0,0 +1,5 @@
+      INTEGER*4 a,b,c
+      READ (*,*) a, b
+      c = a + b
+      WRITE (*,*) c
+      END
diff -ruNp ejudge-2.3.18/contest-1/problems/A/solutions/a_gcc.c ejudge/contest-1/problems/A/solutions/a_gcc.c
--- ejudge-2.3.18/contest-1/problems/A/solutions/a_gcc.c	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/contest-1/problems/A/solutions/a_gcc.c	2011-07-11 09:02:00.738343290 +0400
@@ -0,0 +1,10 @@
+#include <stdio.h>
+
+int main(void)
+{
+  int a, b;
+
+  scanf("%d%d", &a, &b);
+  printf("%d\n", a + b);
+  return 0;
+}
diff -ruNp ejudge-2.3.18/contest-1/problems/A/solutions/a_gcj.java ejudge/contest-1/problems/A/solutions/a_gcj.java
--- ejudge-2.3.18/contest-1/problems/A/solutions/a_gcj.java	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/contest-1/problems/A/solutions/a_gcj.java	2011-07-11 09:02:00.665344630 +0400
@@ -0,0 +1,23 @@
+import java.io.*;
+
+public final class Main
+{
+  public static void main(String args[])
+  {
+    StreamTokenizer st = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
+    int a, b;
+
+    st.resetSyntax();
+    st.eolIsSignificant(false);
+    st.wordChars(33, 255);
+    st.whitespaceChars(0, 32);
+
+    try {
+      st.nextToken(); a = Integer.parseInt(st.sval);
+      st.nextToken(); b = Integer.parseInt(st.sval);
+      System.out.println(a + b);
+    } catch (Exception x) {
+      System.exit(1);
+    }
+  }
+}
diff -ruNp ejudge-2.3.18/contest-1/problems/A/solutions/a_g++.cpp ejudge/contest-1/problems/A/solutions/a_g++.cpp
--- ejudge-2.3.18/contest-1/problems/A/solutions/a_g++.cpp	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/contest-1/problems/A/solutions/a_g++.cpp	2011-07-11 09:02:00.695344079 +0400
@@ -0,0 +1,10 @@
+#include <iostream>
+using namespace std;
+int main()
+{
+  int a, b;
+  cin >> a;
+  cin >> b;
+  cout << a + b;
+  return 0;
+}
diff -ruNp ejudge-2.3.18/contest-1/problems/A/solutions/a_gfortran.for ejudge/contest-1/problems/A/solutions/a_gfortran.for
--- ejudge-2.3.18/contest-1/problems/A/solutions/a_gfortran.for	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/contest-1/problems/A/solutions/a_gfortran.for	2011-07-11 09:02:00.710343805 +0400
@@ -0,0 +1,5 @@
+      INTEGER*4 a,b,c
+      READ (*,*) a, b
+      c = a + b
+      WRITE (*,*) c
+      END
diff -ruNp ejudge-2.3.18/contest-1/problems/A/solutions/a_ghc.hs ejudge/contest-1/problems/A/solutions/a_ghc.hs
--- ejudge-2.3.18/contest-1/problems/A/solutions/a_ghc.hs	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/contest-1/problems/A/solutions/a_ghc.hs	2011-07-11 09:02:00.726343511 +0400
@@ -0,0 +1,3 @@
+main :: IO ()
+main = getContents >>= print . sum . map read . words
+
diff -ruNp ejudge-2.3.18/contest-1/problems/A/solutions/a_gpc.pas ejudge/contest-1/problems/A/solutions/a_gpc.pas
--- ejudge-2.3.18/contest-1/problems/A/solutions/a_gpc.pas	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/contest-1/problems/A/solutions/a_gpc.pas	2011-07-11 09:02:00.693344117 +0400
@@ -0,0 +1,8 @@
+program sum;
+var a, b, c : longint;
+begin
+   readln(a);
+   readln(b);
+   c := a + b;
+   writeln(c);
+end.
diff -ruNp ejudge-2.3.18/contest-1/problems/A/solutions/a_gprolog.pro ejudge/contest-1/problems/A/solutions/a_gprolog.pro
--- ejudge-2.3.18/contest-1/problems/A/solutions/a_gprolog.pro	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/contest-1/problems/A/solutions/a_gprolog.pro	2011-07-11 09:02:00.711343787 +0400
@@ -0,0 +1,4 @@
+% -*- prolog -*-
+:- initialization(main).
+main :- read_number(A), read_number(B), C is A + B, write(C), nl, halt.
+
diff -ruNp ejudge-2.3.18/contest-1/problems/A/solutions/a_javac_sc.java ejudge/contest-1/problems/A/solutions/a_javac_sc.java
--- ejudge-2.3.18/contest-1/problems/A/solutions/a_javac_sc.java	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/contest-1/problems/A/solutions/a_javac_sc.java	2011-07-11 09:02:00.683344300 +0400
@@ -0,0 +1,15 @@
+import java.io.*;
+import java.util.Scanner;
+
+public final class a_b
+{
+  public static void main(String args[]) throws Exception
+  {
+    Scanner s = new Scanner(System.in);
+    int a, b;
+
+    a = s.nextInt();
+    b = s.nextInt();
+    System.out.println(a + b);
+  }
+}
diff -ruNp ejudge-2.3.18/contest-1/problems/A/solutions/a_javac_st.java ejudge/contest-1/problems/A/solutions/a_javac_st.java
--- ejudge-2.3.18/contest-1/problems/A/solutions/a_javac_st.java	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/contest-1/problems/A/solutions/a_javac_st.java	2011-07-11 09:02:00.717343676 +0400
@@ -0,0 +1,23 @@
+import java.io.*;
+
+public final class a_4
+{
+  public static void main(String args[])
+  {
+    StreamTokenizer st = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
+    int a, b;
+
+    st.resetSyntax();
+    st.eolIsSignificant(false);
+    st.wordChars(33, 255);
+    st.whitespaceChars(0, 32);
+
+    try {
+      st.nextToken(); a = Integer.parseInt(st.sval);
+      st.nextToken(); b = Integer.parseInt(st.sval);
+      System.out.println(a + b);
+    } catch (Exception x) {
+      System.exit(1);
+    }
+  }
+}
Binary files ejudge-2.3.18/contest-1/problems/A/solutions/a_kumir.kum and ejudge/contest-1/problems/A/solutions/a_kumir.kum differ
diff -ruNp ejudge-2.3.18/contest-1/problems/A/solutions/a_mcs.cs ejudge/contest-1/problems/A/solutions/a_mcs.cs
--- ejudge-2.3.18/contest-1/problems/A/solutions/a_mcs.cs	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/contest-1/problems/A/solutions/a_mcs.cs	2011-07-11 09:02:00.666344612 +0400
@@ -0,0 +1,11 @@
+using System;
+
+class MainClass
+{
+	static void Main(string[] args)
+	{
+		int a = Int32.Parse(Console.ReadLine());
+		int b = Int32.Parse(Console.ReadLine());
+		Console.WriteLine("{0}", a + b);
+	}
+}
diff -ruNp ejudge-2.3.18/contest-1/problems/A/solutions/a_mzscheme.scm ejudge/contest-1/problems/A/solutions/a_mzscheme.scm
--- ejudge-2.3.18/contest-1/problems/A/solutions/a_mzscheme.scm	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/contest-1/problems/A/solutions/a_mzscheme.scm	2011-07-11 09:02:00.694344098 +0400
@@ -0,0 +1,3 @@
+(display (+ (read) (read)))
+(newline)
+
diff -ruNp ejudge-2.3.18/contest-1/problems/A/solutions/a_perl.pl ejudge/contest-1/problems/A/solutions/a_perl.pl
--- ejudge-2.3.18/contest-1/problems/A/solutions/a_perl.pl	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/contest-1/problems/A/solutions/a_perl.pl	2011-07-11 09:02:00.729343456 +0400
@@ -0,0 +1,3 @@
+chomp($a = <STDIN>);
+chomp($b = <STDIN>);
+print $a + $b;
diff -ruNp ejudge-2.3.18/contest-1/problems/A/solutions/a_php.php ejudge/contest-1/problems/A/solutions/a_php.php
--- ejudge-2.3.18/contest-1/problems/A/solutions/a_php.php	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/contest-1/problems/A/solutions/a_php.php	2011-07-11 09:02:00.739343271 +0400
@@ -0,0 +1,5 @@
+<?php
+list ($a) = fscanf(STDIN, "%d");
+list ($b) = fscanf(STDIN, "%d");
+print $a + $b . "\n";
+?>
diff -ruNp ejudge-2.3.18/contest-1/problems/A/solutions/a_python3.py ejudge/contest-1/problems/A/solutions/a_python3.py
--- ejudge-2.3.18/contest-1/problems/A/solutions/a_python3.py	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/contest-1/problems/A/solutions/a_python3.py	2011-07-11 09:02:00.691344153 +0400
@@ -0,0 +1,3 @@
+a = int(input())
+b = int(input())
+print(a + b)
diff -ruNp ejudge-2.3.18/contest-1/problems/A/solutions/a_python.py ejudge/contest-1/problems/A/solutions/a_python.py
--- ejudge-2.3.18/contest-1/problems/A/solutions/a_python.py	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/contest-1/problems/A/solutions/a_python.py	2011-07-11 09:02:00.714343730 +0400
@@ -0,0 +1,3 @@
+a = int(raw_input())
+b = int(raw_input())
+print a + b
diff -ruNp ejudge-2.3.18/contest-1/problems/A/solutions/a_ruby.rb ejudge/contest-1/problems/A/solutions/a_ruby.rb
--- ejudge-2.3.18/contest-1/problems/A/solutions/a_ruby.rb	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/contest-1/problems/A/solutions/a_ruby.rb	2011-07-11 09:02:00.663344666 +0400
@@ -0,0 +1,3 @@
+a = gets.chomp
+b = gets.chomp
+puts a.to_i + b.to_i
diff -ruNp ejudge-2.3.18/contest-1/problems/A/solutions/a_tpc.pas ejudge/contest-1/problems/A/solutions/a_tpc.pas
--- ejudge-2.3.18/contest-1/problems/A/solutions/a_tpc.pas	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/contest-1/problems/A/solutions/a_tpc.pas	2011-07-11 09:02:00.715343712 +0400
@@ -0,0 +1,8 @@
+program sum;
+var a, b, c : longint;
+begin
+   readln(a);
+   readln(b);
+   c := a + b;
+   writeln(c);
+end.
diff -ruNp ejudge-2.3.18/contest-1/problems/A/solutions/a_vbnc.vb ejudge/contest-1/problems/A/solutions/a_vbnc.vb
--- ejudge-2.3.18/contest-1/problems/A/solutions/a_vbnc.vb	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/contest-1/problems/A/solutions/a_vbnc.vb	2011-07-11 09:02:00.697344042 +0400
@@ -0,0 +1,9 @@
+Module ab
+Sub Main()
+Dim a, b, c as Integer
+a = Console.ReadLine()
+b = Console.ReadLine()
+c = a + b
+Console.WriteLine(c)
+End Sub
+End Module
diff -ruNp ejudge-2.3.18/contest-1/problems/A/solutions/a_yabasic.bas ejudge/contest-1/problems/A/solutions/a_yabasic.bas
--- ejudge-2.3.18/contest-1/problems/A/solutions/a_yabasic.bas	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/contest-1/problems/A/solutions/a_yabasic.bas	2011-07-11 09:02:00.651344887 +0400
@@ -0,0 +1,4 @@
+INPUT "" a
+INPUT "" b
+c = a + b
+PRINT c
diff -ruNp ejudge-2.3.18/contest-1/problems/A/statement.xml ejudge/contest-1/problems/A/statement.xml
--- ejudge-2.3.18/contest-1/problems/A/statement.xml	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/contest-1/problems/A/statement.xml	2011-07-11 09:02:00.946339473 +0400
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<problem
+   package = "ru.ejudge.sample_contest"
+   id = "A"
+   type = "standard">
+  <statement language="ru_RU">
+    <title>Задача A</title>
+    <description>
+<p>На стандартном потоке ввода задаются два целых числа, не меньшие
+-32000 и не большие 32000.
+На стандартный поток вывода напечатайте сумму этих чисел.
+</p>
+<p>
+Числа задаются по одному в строке. Пробельные символы перед числом и после
+него отсутствуют. Пустые строки в вводе отсутствуют.
+</p>
+    </description>
+  </statement>
+  <examples>
+    <example>
+      <input>1
+2</input>
+      <output>3</output>
+    </example>
+  </examples>
+</problem>
diff -ruNp ejudge-2.3.18/contest-1/problems/A/tests/001.ans ejudge/contest-1/problems/A/tests/001.ans
--- ejudge-2.3.18/contest-1/problems/A/tests/001.ans	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/contest-1/problems/A/tests/001.ans	2011-07-11 09:02:00.800342152 +0400
@@ -0,0 +1 @@
+2
diff -ruNp ejudge-2.3.18/contest-1/problems/A/tests/001.dat ejudge/contest-1/problems/A/tests/001.dat
--- ejudge-2.3.18/contest-1/problems/A/tests/001.dat	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/contest-1/problems/A/tests/001.dat	2011-07-11 09:02:00.810341969 +0400
@@ -0,0 +1,3 @@
+1
+1
+
diff -ruNp ejudge-2.3.18/contest-1/problems/A/tests/002.ans ejudge/contest-1/problems/A/tests/002.ans
--- ejudge-2.3.18/contest-1/problems/A/tests/002.ans	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/contest-1/problems/A/tests/002.ans	2011-07-11 09:02:00.802342116 +0400
@@ -0,0 +1 @@
+5
diff -ruNp ejudge-2.3.18/contest-1/problems/A/tests/002.dat ejudge/contest-1/problems/A/tests/002.dat
--- ejudge-2.3.18/contest-1/problems/A/tests/002.dat	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/contest-1/problems/A/tests/002.dat	2011-07-11 09:02:00.816341859 +0400
@@ -0,0 +1,2 @@
+2
+3
diff -ruNp ejudge-2.3.18/contest-1/problems/A/tests/003.ans ejudge/contest-1/problems/A/tests/003.ans
--- ejudge-2.3.18/contest-1/problems/A/tests/003.ans	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/contest-1/problems/A/tests/003.ans	2011-07-11 09:02:00.803342098 +0400
@@ -0,0 +1 @@
+0
diff -ruNp ejudge-2.3.18/contest-1/problems/A/tests/003.dat ejudge/contest-1/problems/A/tests/003.dat
--- ejudge-2.3.18/contest-1/problems/A/tests/003.dat	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/contest-1/problems/A/tests/003.dat	2011-07-11 09:02:00.817341840 +0400
@@ -0,0 +1,2 @@
+-4
+4
diff -ruNp ejudge-2.3.18/contest-1/problems/A/tests/004.ans ejudge/contest-1/problems/A/tests/004.ans
--- ejudge-2.3.18/contest-1/problems/A/tests/004.ans	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/contest-1/problems/A/tests/004.ans	2011-07-11 09:02:00.806342043 +0400
@@ -0,0 +1 @@
+8
diff -ruNp ejudge-2.3.18/contest-1/problems/A/tests/004.dat ejudge/contest-1/problems/A/tests/004.dat
--- ejudge-2.3.18/contest-1/problems/A/tests/004.dat	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/contest-1/problems/A/tests/004.dat	2011-07-11 09:02:00.819341804 +0400
@@ -0,0 +1,2 @@
+4
+4
diff -ruNp ejudge-2.3.18/contest-1/problems/A/tests/005.ans ejudge/contest-1/problems/A/tests/005.ans
--- ejudge-2.3.18/contest-1/problems/A/tests/005.ans	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/contest-1/problems/A/tests/005.ans	2011-07-11 09:02:00.808342005 +0400
@@ -0,0 +1 @@
+5730
\ No newline at end of file
diff -ruNp ejudge-2.3.18/contest-1/problems/A/tests/005.dat ejudge/contest-1/problems/A/tests/005.dat
--- ejudge-2.3.18/contest-1/problems/A/tests/005.dat	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/contest-1/problems/A/tests/005.dat	2011-07-11 09:02:00.821341768 +0400
@@ -0,0 +1,2 @@
+1231
+4499
diff -ruNp ejudge-2.3.18/contest-1/problems/B/solutions/b_gcc.c ejudge/contest-1/problems/B/solutions/b_gcc.c
--- ejudge-2.3.18/contest-1/problems/B/solutions/b_gcc.c	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/contest-1/problems/B/solutions/b_gcc.c	2011-07-11 09:02:00.979338868 +0400
@@ -0,0 +1,11 @@
+#include <stdio.h>
+
+int main(void)
+{
+  int s = 0, x;
+
+  while (scanf("%d", &x) == 1)
+    s += x;
+  printf("%d\n", s);
+  return 0;
+}
diff -ruNp ejudge-2.3.18/contest-1/problems/B/statement.xml ejudge/contest-1/problems/B/statement.xml
--- ejudge-2.3.18/contest-1/problems/B/statement.xml	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/contest-1/problems/B/statement.xml	2011-07-11 09:02:01.224334372 +0400
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<problem
+   package = "ru.ejudge.sample_contest"
+   id = "B"
+   type = "standard">
+  <statement language="ru_RU">
+    <title>Задача B</title>
+    <description>
+<p>На стандартном потоке ввода задаётся последовательность целых чисел.
+Каждое число последовательности не меньше -20000000 и не больше 20000000.
+Всего последовательность состоит не более чем из 40 чисел.
+Последовательность завершается признаком конца ввода.
+На стандартный поток вывода напечатайте сумму этих чисел.
+</p>
+    </description>
+  </statement>
+  <examples>
+    <example>
+      <input>1 2 3 4</input>
+      <output>10</output>
+    </example>
+  </examples>
+</problem>
diff -ruNp ejudge-2.3.18/contest-1/problems/B/tests/001.ans ejudge/contest-1/problems/B/tests/001.ans
--- ejudge-2.3.18/contest-1/problems/B/tests/001.ans	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/contest-1/problems/B/tests/001.ans	2011-07-11 09:02:01.140335913 +0400
@@ -0,0 +1 @@
+15
diff -ruNp ejudge-2.3.18/contest-1/problems/B/tests/001.dat ejudge/contest-1/problems/B/tests/001.dat
--- ejudge-2.3.18/contest-1/problems/B/tests/001.dat	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/contest-1/problems/B/tests/001.dat	2011-07-11 09:02:01.143335859 +0400
@@ -0,0 +1,5 @@
+1
+2
+3
+4
+5
diff -ruNp ejudge-2.3.18/contest-1/problems/B/tests/002.ans ejudge/contest-1/problems/B/tests/002.ans
--- ejudge-2.3.18/contest-1/problems/B/tests/002.ans	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/contest-1/problems/B/tests/002.ans	2011-07-11 09:02:01.141335895 +0400
@@ -0,0 +1 @@
+996
diff -ruNp ejudge-2.3.18/contest-1/problems/B/tests/002.dat ejudge/contest-1/problems/B/tests/002.dat
--- ejudge-2.3.18/contest-1/problems/B/tests/002.dat	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/contest-1/problems/B/tests/002.dat	2011-07-11 09:02:01.144335840 +0400
@@ -0,0 +1,5 @@
+100
+200
+300
+400
+-4
diff -ruNp ejudge-2.3.18/contest-1/problems/B/tests/003.ans ejudge/contest-1/problems/B/tests/003.ans
--- ejudge-2.3.18/contest-1/problems/B/tests/003.ans	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/contest-1/problems/B/tests/003.ans	2011-07-11 09:02:01.142335877 +0400
@@ -0,0 +1 @@
+99556547
diff -ruNp ejudge-2.3.18/contest-1/problems/B/tests/003.dat ejudge/contest-1/problems/B/tests/003.dat
--- ejudge-2.3.18/contest-1/problems/B/tests/003.dat	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/contest-1/problems/B/tests/003.dat	2011-07-11 09:02:01.145335821 +0400
@@ -0,0 +1,12 @@
+11111111
+1222222
+12222222
+19999999
+19999999
+1999999
+19999999
+
+1999999
+9999999
+999999
+999
diff -ruNp ejudge-2.3.18/contests_2.c ejudge/contests_2.c
--- ejudge-2.3.18/contests_2.c	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/contests_2.c	2011-07-11 09:02:04.697270569 +0400
@@ -1,5 +1,5 @@
 /* -*- mode: c -*- */
-/* $Id: contests_2.c 6162 2011-03-27 07:07:27Z cher $ */
+/* $Id: contests_2.c 6351 2011-05-24 19:28:45Z cher $ */
 
 /* Copyright (C) 2008-2011 Alexander Chernov <cher@ejudge.ru> */
 
@@ -264,6 +264,7 @@ contests_unparse(FILE *f,
     CONTEST_REGISTER_EMAIL, CONTEST_REGISTER_URL, CONTEST_TEAM_URL,
     CONTEST_STANDINGS_URL, CONTEST_PROBLEMS_URL, CONTEST_REGISTER_EMAIL_FILE,
     CONTEST_LOGIN_TEMPLATE, CONTEST_LOGIN_TEMPLATE_OPTIONS,
+    CONTEST_LOGO_URL, CONTEST_CSS_URL,
     0,
   });
 
diff -ruNp ejudge-2.3.18/contests.c ejudge/contests.c
--- ejudge-2.3.18/contests.c	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/contests.c	2011-07-11 09:02:04.108281398 +0400
@@ -1,5 +1,5 @@
 /* -*- mode: c -*- */
-/* $Id: contests.c 6162 2011-03-27 07:07:27Z cher $ */
+/* $Id: contests.c 6349 2011-05-24 05:20:31Z cher $ */
 
 /* Copyright (C) 2002-2011 Alexander Chernov <cher@ejudge.ru> */
 
@@ -125,6 +125,8 @@ const int contests_tag_to_meta_map[CONTE
   [CONTEST_WELCOME_FILE] = CNTS_welcome_file,
   [CONTEST_REG_WELCOME_FILE] = CNTS_reg_welcome_file,
   [CONTEST_USER_CONTEST] = CNTS_user_contest,
+  [CONTEST_LOGO_URL] = CNTS_logo_url,
+  [CONTEST_CSS_URL] = CNTS_css_url,
 };
 const int contests_attr_to_meta_map[CONTEST_LAST_ATTR] =
 {
@@ -235,6 +237,8 @@ char const * const contests_elem_map[] =
   "slave_rules",
   "run_managed_on",
   "user_contest",
+  "logo_url",
+  "css_url",
 
   0
 };
@@ -753,6 +757,8 @@ static const unsigned char contest_final
   [CONTEST_WELCOME_FILE] = 1,
   [CONTEST_REG_WELCOME_FILE] = 1,
   [CONTEST_USER_CONTEST] = 1,
+  [CONTEST_LOGO_URL] = 1,
+  [CONTEST_CSS_URL] = 1,
 };
 
 static const unsigned char contest_access_set[CONTEST_LAST_TAG] =
@@ -1714,12 +1720,7 @@ contests_get_path_in_conf_dir(
     snprintf(home_dir, sizeof(home_dir), "%s", CONTESTS_HOME_DIR);
 #endif
     if (!home_dir[0]) {
-#ifdef __WIN32__
-      /* INSERT YOUR SERVER'S SAMBA ADDRESS HERE ------\/-----------------*/
-      snprintf(home_dir, sizeof(home_dir), "%s", "//192.168.244.130/judges");
-#else
       snprintf(home_dir, sizeof(home_dir), "%s", "/home/judges");
-#endif /* __WIN32__ */
     }
     if (cnts->root_dir) {
       snprintf(root_dir, sizeof(root_dir), "%s/%s", home_dir, cnts->root_dir);
diff -ruNp ejudge-2.3.18/contests.h ejudge/contests.h
--- ejudge-2.3.18/contests.h	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/contests.h	2011-07-11 09:02:04.139280826 +0400
@@ -1,10 +1,10 @@
 /* -*- c -*- */
-/* $Id: contests.h 5675 2010-01-19 09:52:11Z cher $ */
+/* $Id: contests.h 6349 2011-05-24 05:20:31Z cher $ */
 
 #ifndef __CONTESTS_H__
 #define __CONTESTS_H__
 
-/* Copyright (C) 2002-2009 Alexander Chernov <cher@ejudge.ru> */
+/* Copyright (C) 2002-2011 Alexander Chernov <cher@ejudge.ru> */
 
 /*
  * This program is free software; you can redistribute it and/or modify
@@ -116,6 +116,8 @@ enum
     CONTEST_SLAVE_RULES,
     CONTEST_RUN_MANAGED_ON,
     CONTEST_USER_CONTEST,
+    CONTEST_LOGO_URL,
+    CONTEST_CSS_URL,
 
     CONTEST_LAST_TAG
   };
@@ -368,6 +370,8 @@ struct contest_desc
   unsigned char *default_locale;
   unsigned char *welcome_file;
   unsigned char *reg_welcome_file;
+  unsigned char *logo_url;
+  unsigned char *css_url;
 
   struct xml_tree *slave_rules;
 
diff -ruNp ejudge-2.3.18/contests_meta.c ejudge/contests_meta.c
--- ejudge-2.3.18/contests_meta.c	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/contests_meta.c	2011-07-11 09:02:04.735269870 +0400
@@ -1,5 +1,5 @@
 // This is an auto-generated file, do not edit
-// Generated 2011/03/26 13:51:20
+// Generated 2011/05/24 09:20:57
 
 #include "contests_meta.h"
 #include "contests.h"
@@ -103,6 +103,8 @@ static struct meta_info_item meta_info_c
   [CNTS_default_locale] = { CNTS_default_locale, 's', XSIZE(struct contest_desc, default_locale), "default_locale", XOFFSET(struct contest_desc, default_locale) },
   [CNTS_welcome_file] = { CNTS_welcome_file, 's', XSIZE(struct contest_desc, welcome_file), "welcome_file", XOFFSET(struct contest_desc, welcome_file) },
   [CNTS_reg_welcome_file] = { CNTS_reg_welcome_file, 's', XSIZE(struct contest_desc, reg_welcome_file), "reg_welcome_file", XOFFSET(struct contest_desc, reg_welcome_file) },
+  [CNTS_logo_url] = { CNTS_logo_url, 's', XSIZE(struct contest_desc, logo_url), "logo_url", XOFFSET(struct contest_desc, logo_url) },
+  [CNTS_css_url] = { CNTS_css_url, 's', XSIZE(struct contest_desc, css_url), "css_url", XOFFSET(struct contest_desc, css_url) },
   [CNTS_slave_rules] = { CNTS_slave_rules, '?', XSIZE(struct contest_desc, slave_rules), "slave_rules", XOFFSET(struct contest_desc, slave_rules) },
   [CNTS_user_contest_num] = { CNTS_user_contest_num, 'i', XSIZE(struct contest_desc, user_contest_num), "user_contest_num", XOFFSET(struct contest_desc, user_contest_num) },
   [CNTS_default_locale_num] = { CNTS_default_locale_num, 'i', XSIZE(struct contest_desc, default_locale_num), "default_locale_num", XOFFSET(struct contest_desc, default_locale_num) },
@@ -148,6 +150,8 @@ int contest_desc_lookup_field(const char
 
 const struct meta_methods contest_desc_methods =
 {
+  CNTS_LAST_FIELD,
+  sizeof(struct contest_desc),
   contest_desc_get_type,
   contest_desc_get_size,
   contest_desc_get_name,
diff -ruNp ejudge-2.3.18/contests_meta.h ejudge/contests_meta.h
--- ejudge-2.3.18/contests_meta.h	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/contests_meta.h	2011-07-11 09:02:04.741269758 +0400
@@ -1,5 +1,5 @@
 // This is an auto-generated file, do not edit
-// Generated 2011/03/26 13:51:20
+// Generated 2011/05/24 09:20:57
 
 #ifndef __CONTESTS_META_H__
 #define __CONTESTS_META_H__
@@ -98,6 +98,8 @@ enum
   CNTS_default_locale,
   CNTS_welcome_file,
   CNTS_reg_welcome_file,
+  CNTS_logo_url,
+  CNTS_css_url,
   CNTS_slave_rules,
   CNTS_user_contest_num,
   CNTS_default_locale_num,
diff -ruNp ejudge-2.3.18/contest_tmpl.c ejudge/contest_tmpl.c
--- ejudge-2.3.18/contest_tmpl.c	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/contest_tmpl.c	2011-07-11 09:02:04.189279907 +0400
@@ -1,5 +1,5 @@
 /* -*- mode: c -*- */
-/* $Id: contest_tmpl.c 6162 2011-03-27 07:07:27Z cher $ */
+/* $Id: contest_tmpl.c 6349 2011-05-24 05:20:31Z cher $ */
 
 /* Copyright (C) 2005-2011 Alexander Chernov <cher@ejudge.ru> */
 
@@ -268,6 +268,8 @@ contest_tmpl_clone(struct sid_state *sst
   subst_param(&cnts->register_email_file, 6, substs_from, substs_to);
   subst_param(&cnts->main_url, 6, substs_from, substs_to);
   subst_param(&cnts->standings_url, 6, substs_from, substs_to);
+  subst_param(&cnts->logo_url, 6, substs_from, substs_to);
+  subst_param(&cnts->css_url, 6, substs_from, substs_to);
 
   for (cap = cnts->capabilities.first; cap; cap = (typeof(cap)) cap->b.right)
     if (!strcmp(cap->login, login))
diff -ruNp ejudge-2.3.18/db/revisions ejudge/db/revisions
--- ejudge-2.3.18/db/revisions	2011-04-10 09:25:21.000000000 +0400
+++ ejudge/db/revisions	2011-07-11 09:02:02.796305506 +0400
@@ -6216,3 +6216,244 @@ run.c 6239 0 10053548
 super_html.c 6240 0 12260055
 super_html_3.c 6240 0 30771560
 super_html.c 6242 0 12097703
+misctext.h 6246 0 689392
+misctext.c 6246 0 3338994
+parsecfg.c 6246 0 3548610
+prepare_out.c 6246 0 7398283
+super_html_3.c 6246 0 30709637
+super_html_3.c 6247 0 30683591
+super_html.h 6251 0 1539480
+super_proto.h 6251 0 2395028
+super-serve.h 6251 0 631866
+uldb_plugin.h 6251 0 872825
+userlist_clnt.h 6251 0 991552
+super-serve.c 6251 0 14207246
+userlist-server.c 6251 0 24133296
+super_html.c 6251 0 12116011
+super_html_4.c 6251 0 18955815
+super_html_6.c 6251 0 456364
+uldb_plugin_xml.c 6251 0 8962741
+userlist_clnt/list_users_2.c 6249 0 209065
+plugins/mysql-userlist/uldb_mysql.c 6250 0 11878916
+super_html.h 6255 0 1549479
+uldb_plugin.h 6255 0 886375
+userlist_clnt.h 6255 0 1005115
+userlist-server.c 6255 0 24235899
+super_html_4.c 6255 0 19004749
+super_html_6.c 6255 0 732182
+uldb_plugin_xml.c 6255 0 9008525
+userlist_clnt/get_count.c 6254 0 193447
+plugins/mysql-userlist/uldb_mysql.c 6253 0 11973873
+super_html.h 6256 0 1559462
+userlist-server.c 6256 0 24237345
+super_html_4.c 6256 0 19004067
+super_html_6.c 6256 0 846625
+userlist_clnt/get_count.c 6257 0 193404
+plugins/mysql-userlist/uldb_mysql.c 6259 0 11986580
+plugins/mysql-userlist/uldb_mysql.c 6261 0 12218698
+userlist_clnt/get_count.c 6262 0 197361
+userlist_clnt/list_users_2.c 6262 0 213018
+super_html.h 6263 0 1579394
+super_proto.h 6263 0 2414165
+super-serve.h 6263 0 665207
+uldb_plugin.h 6263 0 912042
+userlist_clnt.h 6263 0 1008181
+userlist-server.c 6263 0 24535115
+super_html.c 6263 0 12173522
+super_html_4.c 6263 0 19040419
+super_html_6.c 6263 0 971750
+uldb_plugin_xml.c 6263 0 9307672
+userlist.c 6263 0 4739285
+super_html.h 6265 0 1603441
+super_proto.h 6265 0 2457713
+super_html.c 6265 0 12175045
+super_html_4.c 6265 0 19054832
+super_html_6.c 6265 0 3931406
+super_html.h 6267 0 1655834
+super_proto.h 6267 0 2479263
+super_html_4.c 6267 0 19087012
+super_html_6.c 6267 0 7010156
+super_html.h 6271 0 1687943
+super_proto.h 6271 0 2490346
+super_html_4.c 6271 0 19107494
+super_html_6.c 6271 0 9682141
+ejudge-setup.c 6273 0 10145660
+super_html.h 6282 0 1709914
+super_html_4.c 6282 0 19144860
+super_html_6.c 6282 0 11477018
+super_html.h 6284 0 1564459
+super_proto.h 6284 0 2490185
+userlist-server.c 6284 0 24862514
+super_html.c 6284 0 12174706
+super_html_4.c 6284 0 19707710
+super_html_6.c 6284 0 11859839
+ej_types.h 6286 0 234545
+meta_generic.h 6286 0 130035
+super_html.h 6286 0 1566199
+meta_generic.c 6286 0 295747
+super_html_4.c 6286 0 19711925
+super_html_6.c 6286 0 12117220
+super_html.h 6290 0 1569611
+uldb_plugin.h 6290 0 932594
+userlist_clnt.h 6290 0 1043669
+userlist-server.c 6290 0 26147918
+super_html_4.c 6290 0 19719920
+super_html_6.c 6290 0 12354820
+uldb_plugin_xml.c 6290 0 9370951
+userlist_clnt/create_user_2.c 6289 0 452792
+plugins/mysql-userlist/uldb_mysql.c 6288 0 12296065
+super_html_6.c 6292 0 12359925
+userlist-server.c 6296 0 26167917
+super_html_6.c 6296 0 12442034
+userlist_clnt/create_user_2.c 6296 0 452944
+super_html_6.c 6298 0 12699783
+super_html_6.c 6300 0 12707738
+ej_types.h 6302 0 237285
+super_html.h 6302 0 1576662
+super_html_4.c 6302 0 19738452
+super_html_6.c 6302 0 12938736
+super_html.h 6303 0 1584314
+super_html_4.c 6303 0 19756462
+super_html_6.c 6303 0 13605520
+super_html_6.c 6305 0 13605521
+super_html_6.c 6307 0 13606317
+super_html.h 6309 0 1589014
+super_html_4.c 6309 0 19767477
+super_html_6.c 6309 0 14310266
+super-serve.c 6311 0 14217760
+super_proto.h 6314 0 2502892
+uldb_plugin.h 6314 0 958745
+userlist_clnt.h 6316 0 1060550
+userlist-server.c 6314 0 26310172
+super_html_4.c 6314 0 19777805
+super_html_6.c 6314 0 15329443
+uldb_plugin_xml.c 6314 0 9555072
+userlist.c 6314 0 4741286
+userlist_clnt/get_prev_user_id.c 6315 0 200317
+plugins/mysql-userlist/uldb_mysql.c 6313 0 12588849
+super_html.h 6318 0 1590720
+super_proto.h 6318 0 2507622
+super_html_4.c 6318 0 19792289
+super_html_6.c 6318 0 16157338
+super_html_6.c 6321 0 16157333
+userlist_clnt/get_prev_user_id.c 6321 0 202522
+mischtml.h 6323 0 197562
+compile.c 6323 0 2352086
+ejudge-setup.c 6323 0 10145893
+job-server.c 6323 0 1322997
+new-server.c 6323 0 1827517
+super-serve.c 6323 0 14220052
+html_input_text.c 6323 0 200914
+super_html.c 6323 0 11721019
+super_html_4.c 6323 0 19792345
+super_html_6.c 6323 0 16255558
+lang_config_vis.h 6325 0 194875
+ejudge-configure-compilers.c 6325 0 1692425
+ejudge-setup.c 6325 0 10263154
+lang_config_vis.c 6325 0 2245746
+super_html.h 6326 0 1592596
+super_html_4.c 6326 0 19796160
+super_html_6.c 6326 0 16463191
+super_proto.h 6328 0 2511086
+super_html_6.c 6328 0 16466322
+super_html.h 6330 0 1593993
+super_proto.h 6330 0 2512578
+super_html_4.c 6330 0 19804416
+super_html_6.c 6330 0 16706012
+super_proto.h 6333 0 2513463
+userlist-server.c 6333 0 26310181
+super_html_6.c 6333 0 16448467
+uldb_plugin_xml.c 6333 0 9558039
+plugins/mysql-userlist/uldb_mysql.c 6332 0 12625041
+super_proto.h 6335 0 2517783
+super_html_6.c 6335 0 16885965
+super_html_6.c 6337 0 17225465
+super_html_6.c 6339 0 17585071
+html.h 6341 0 564770
+html.c 6341 0 14189613
+new_server_html.c 6341 0 39899424
+new_server_html_2.c 6341 0 16573488
+html.h 6343 0 575404
+mischtml.h 6343 0 221787
+html.c 6343 0 14399533
+html_hyperref.c 6343 0 212435
+new_server_html.c 6343 0 39974563
+new_server_html_2.c 6343 0 16579788
+prepare.c 6345 0 18782046
+sformat.c 6345 0 1817744
+misctext.h 6347 0 695511
+super_html.h 6347 0 1595322
+ejudge-setup.c 6347 0 10299757
+job-server.c 6347 0 1360702
+misctext.c 6347 0 3421863
+new_server_html_5.c 6347 0 9251098
+super_html_2.c 6347 0 4968404
+super_html_4.c 6347 0 19828577
+super_html_6.c 6347 0 17614743
+contests.h 6349 0 1201675
+contests.c 6349 0 4181588
+contest_tmpl.c 6349 0 945040
+super_proto.h 6351 0 2527479
+serve-control.c 6351 0 9423794
+super-serve.c 6351 0 14251132
+contests_2.c 6351 0 2045843
+super_html.c 6351 0 11773017
+super_html_2.c 6351 0 4991460
+super_html_3.c 6351 0 30688511
+super_html_4.c 6351 0 19844729
+new-server.h 6353 0 2301505
+new_server_html.c 6353 0 40002962
+new_server_html_2.c 6353 0 16581350
+new_server_html_3.c 6353 0 2781347
+new_server_html_5.c 6353 0 9260316
+bitset.h 6355 0 215575
+super_proto.h 6356 0 2588357
+bitset.c 6355 0 690834
+super_html_4.c 6356 0 19969809
+super_html_6.c 6356 0 18520483
+super_proto.h 6359 0 2590915
+super-serve.h 6359 0 668554
+super-serve.c 6359 0 14253386
+userlist-server.c 6359 0 26463581
+super_html_4.c 6359 0 20006342
+super_html_6.c 6359 0 19944503
+super_html_4.c 6361 0 20065630
+super_html_6.c 6361 0 21201248
+super_proto.h 6363 0 2603806
+userlist-server.c 6363 0 26621874
+super_html_4.c 6363 0 20102717
+super_html_6.c 6363 0 21855687
+super_html_6.c 6365 0 21862165
+userlist-server.c 6368 0 26622835
+unix/reuse_exec.c 6367 0 4420560
+serve_2.c 6368 0 9620332
+super_proto.h 6370 0 2606581
+super_html_4.c 6370 0 20107779
+super_html_6.c 6370 0 21972060
+super_proto.h 6372 0 2620181
+super_html_4.c 6372 0 20132010
+super_html_6.c 6372 0 22489789
+super_proto.h 6374 0 2627956
+super_html_4.c 6374 0 20146088
+super_html_6.c 6374 0 23233824
+super_proto.h 6377 0 2640864
+userlist-server.c 6377 0 26735216
+super_html_4.c 6377 0 20169910
+super_html_6.c 6377 0 23311933
+plugins/mysql-userlist/uldb_mysql.c 6376 0 12879277
+super_html.h 6379 0 1601098
+super_proto.h 6379 0 2644724
+super_html_4.c 6379 0 20216962
+super_html_6.c 6379 0 24425514
+uldb_plugin_xml.c 6379 0 9645022
+new-server.h 6381 0 2315370
+new-server-cmd.c 6381 0 2322130
+new_server_html.c 6381 0 40025683
+new_server_html_4.c 6381 0 5027241
+lang_config_vis.c 6387 0 2252111
+super_html.c 6390 0 11791816
+compile.c 6393 0 2392092
+new_server_html_2.c 6393 0 16583121
+serve_2.c 6393 0 9641561
+super_html_6.c 6398 0 24425640
+super_html_4.c 6401 0 20209972
diff -ruNp ejudge-2.3.18/db/versions ejudge/db/versions
--- ejudge-2.3.18/db/versions	2011-04-10 09:25:21.000000000 +0400
+++ ejudge/db/versions	2011-07-11 09:02:02.797305487 +0400
@@ -166,3 +166,4 @@
 5980 2.3 16 2010/08/17
 6033 2.3 17 2010/11/05
 6242 2.3 18 2011/04/10
+6393 2.3 19 2011/07/06
diff -ruNp ejudge-2.3.18/ej_types.h ejudge/ej_types.h
--- ejudge-2.3.18/ej_types.h	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/ej_types.h	2011-07-11 09:02:04.701270494 +0400
@@ -1,5 +1,5 @@
 /* -*- c -*- */
-/* $Id: ej_types.h 6172 2011-03-27 12:40:30Z cher $ */
+/* $Id: ej_types.h 6302 2011-05-04 04:22:17Z cher $ */
 #ifndef __EJ_TYPES_H__
 #define __EJ_TYPES_H__
 
@@ -34,6 +34,14 @@ typedef int ejintsize_t;
 typedef char **ejstrlist_t;
 typedef char **ejenvlist_t;
 
+/* for CGI param parser */
+typedef int ej_checkbox_t;
+typedef int ej_int_opt_0_t;
+typedef int ej_int_opt_1_t;
+typedef int ej_int_opt_m1_t;
+typedef unsigned char *ej_textbox_t;
+typedef unsigned char *ej_textbox_opt_t;
+
 /* privilege level */
 enum priv_level
 {
diff -ruNp ejudge-2.3.18/ejudge-configure-compilers.c ejudge/ejudge-configure-compilers.c
--- ejudge-2.3.18/ejudge-configure-compilers.c	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/ejudge-configure-compilers.c	2011-07-11 09:02:04.604272277 +0400
@@ -1,5 +1,5 @@
 /* -*- mode:c -*- */
-/* $Id: ejudge-configure-compilers.c 6135 2011-03-26 09:30:42Z cher $ */
+/* $Id: ejudge-configure-compilers.c 6325 2011-05-09 06:47:13Z cher $ */
 
 /* Copyright (C) 2008-2011 Alexander Chernov <cher@ejudge.ru> */
 
@@ -345,7 +345,7 @@ visual_setup(unsigned char **keys, unsig
   script_in_dirs[1] = script_in_dir1;
   script_in_dirs[2] = 0;
   lang_configure_screen(script_dir, script_in_dirs,
-                        config_dir, tmp_work_dir, keys, vals, header);
+                        config_dir, tmp_work_dir, keys, vals, header, 0);
   while (lang_config_menu(script_dir, script_in_dirs, tmp_work_dir,
                           header, utf8_mode, &cur_item));
 
diff -ruNp ejudge-2.3.18/ejudge.ru_RU.KOI8-R.po ejudge/ejudge.ru_RU.KOI8-R.po
--- ejudge-2.3.18/ejudge.ru_RU.KOI8-R.po	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/ejudge.ru_RU.KOI8-R.po	2011-07-11 09:02:04.392276176 +0400
@@ -1,14 +1,14 @@
 # -*- mode: po; coding: koi8-r -*-
 # ejudge messages catalog
-# $Id: ejudge.ru_RU.KOI8-R.po 6120 2011-03-24 20:41:20Z cher $
+# $Id: ejudge.ru_RU.KOI8-R.po 6344 2011-05-22 21:25:17Z cher $
 # Copyright (C) 2002-2011 Alexander Chernov <cher@ejudge.ru>
 #
 msgid ""
 msgstr ""
 "Project-Id-Version: ejudge 2.3\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2011-03-16 23:48+0300\n"
-"PO-Revision-Date: 2011-03-23 08:37+0300\n"
+"POT-Creation-Date: 2011-05-23 01:14+0400\n"
+"PO-Revision-Date: 2011-05-23 01:15+0400\n"
 "Last-Translator: Alexander Chernov <cher@ejudge.ru>\n"
 "Language-Team: Russian <ru@li.org>\n"
 "Language: ru\n"
@@ -962,6 +962,9 @@ msgstr "����������"
 msgid "Coordinator"
 msgstr "�����������"
 
+msgid "Correct answer"
+msgstr "���������� �����"
+
 msgid "Correct output"
 msgstr "���������� �����"
 
@@ -1920,6 +1923,9 @@ msgstr "��������� �������� �����"
 msgid "Link"
 msgstr "������"
 
+msgid "Links"
+msgstr "������"
+
 msgid "List of registered participants"
 msgstr "������ �������������������� ����������"
 
@@ -2606,6 +2612,9 @@ msgstr "����� ���������"
 msgid "Program output to stderr"
 msgstr "����� ��������� �� stderr"
 
+msgid "Program stderr output"
+msgstr "����� ��������� �� ����� ������"
+
 msgid "Programming language"
 msgstr "���� ����������������"
 
@@ -3277,9 +3286,15 @@ msgstr "����������� ���� �� ����������"
 msgid "Test file is not available"
 msgstr "���� ����������"
 
+msgid "Test information"
+msgstr "���������� � �����"
+
 msgid "Test input"
 msgstr "������� ������"
 
+msgid "Test input data"
+msgstr "������� ������"
+
 msgid "Test number"
 msgstr "����� �����"
 
diff -ruNp ejudge-2.3.18/ejudge-setup.c ejudge/ejudge-setup.c
--- ejudge-2.3.18/ejudge-setup.c	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/ejudge-setup.c	2011-07-11 09:02:04.684270806 +0400
@@ -1,5 +1,5 @@
 /* -*- mode:c -*- */
-/* $Id: ejudge-setup.c 6224 2011-04-04 17:56:50Z cher $ */
+/* $Id: ejudge-setup.c 6347 2011-05-23 19:06:12Z cher $ */
 
 /* Copyright (C) 2004-2011 Alexander Chernov <cher@ejudge.ru> */
 
@@ -1097,9 +1097,12 @@ is_valid_id_var(int idx)
   case ID_LINE_RETURN:
   case ID_LINE_SETTINGS:
     return 1;
+  case ID_LINE_EMAIL:
+    if (!is_valid_email_address(id_edit_items[idx].buf)) return 0;
+    if (id_edit_items[idx].buf[0]) return 1;
+    return 0;
   case ID_LINE_USER_ID:
   case ID_LINE_LOGIN:
-  case ID_LINE_EMAIL:
   case ID_LINE_NAME:
     if (id_edit_items[idx].buf[0]) return 1;
     return 0;
@@ -1119,6 +1122,21 @@ valid_id_str(int idx)
   return "!";
 }
 
+static void
+make_sha1_passwd(unsigned char *out_buf, const unsigned char *str)
+{
+  unsigned char buf2[64];
+  int len, j;
+
+  if (!str) str = "";
+  len = strlen(str);
+  memset(buf2, 0, sizeof(buf2));
+  sha_buffer(str, len, buf2);
+  for (j = 0; j < 20; j++) {
+    sprintf(out_buf + j * 2, "%02x", buf2[j]);
+  }
+}
+
 static int
 do_identity_menu(int *p_cur_item)
 {
@@ -1289,10 +1307,7 @@ do_identity_menu(int *p_cur_item)
 
         memset(config_password_txt, '*', j);
         config_password_txt[j] = 0;
-        sha_buffer(buf1, j, buf2);
-        for (j = 0; j < 20; j++) {
-          sprintf(config_password_sha1 + j * 2, "%02x", buf2[j]);
-        }
+        make_sha1_passwd(config_password_sha1, buf1);
         ncurses_msgbox("\\begin{center}\nNOTICE!\n\nThe password sha1 hash is %s!\n\\end{center}\n", config_password_sha1);
 
         cur_item = i;
@@ -1338,6 +1353,10 @@ do_identity_menu(int *p_cur_item)
           ncurses_errbox("\\begin{center}\nERROR!\n\nThe administrator e-mail contains invalid characters!\n\\end{center}\n");
           continue;
         }
+        if (!is_valid_email_address(buf1)) {
+          ncurses_errbox("\\begin{center}\nERROR!\n\nThe administrator e-mail is invalid!\n\\end{center}\n");
+          continue;
+        }
         snprintf(config_email, sizeof(config_email), "%s", buf1);
         break;
       case ID_LINE_NAME:
@@ -1541,7 +1560,7 @@ initialize_setting_var(int idx)
              system_group);
     break;
   case SET_LINE_DEFAULT_CLARDB_PLUGIN:
-#if CONF_HAS_MYSQL - 0 == 1
+#if 0 //CONF_HAS_MYSQL - 0 == 1
     snprintf(config_default_clardb_plugin, sizeof(config_default_clardb_plugin), "%s",
              "mysql");
 #else
@@ -1549,7 +1568,7 @@ initialize_setting_var(int idx)
 #endif
     break;
   case SET_LINE_DEFAULT_RUNDB_PLUGIN:
-#if CONF_HAS_MYSQL - 0 == 1
+#if 0 // CONF_HAS_MYSQL - 0 == 1
     snprintf(config_default_rundb_plugin, sizeof(config_default_rundb_plugin), "%s",
              "mysql");
 #else
@@ -1558,7 +1577,7 @@ initialize_setting_var(int idx)
     break;
   case SET_LINE_WORKDISK_FLAG:
     snprintf(config_workdisk_flag, sizeof(config_workdisk_flag),
-             "%s", "yes");
+             "%s", "no");
     break;
   case SET_LINE_INSTALL_FLAG:
     snprintf(config_install_flag, sizeof(config_install_flag),
@@ -1593,8 +1612,11 @@ is_valid_setting_var(int idx)
   case SET_LINE_DEFAULT_CLARDB_PLUGIN:
   case SET_LINE_DEFAULT_RUNDB_PLUGIN:
     return 1;
-  case SET_LINE_CHARSET:
   case SET_LINE_REG_EMAIL:
+    if (!is_valid_email_address(set_edit_items[idx].buf)) return 0;
+    if (set_edit_items[idx].buf[0]) return 1;
+    return 0;
+  case SET_LINE_CHARSET:
   case SET_LINE_REG_URL:
   case SET_LINE_SER_KEY:
   case SET_LINE_SERVER_NAME:
@@ -2260,6 +2282,7 @@ generate_serve_cfg(FILE *f)
   } else {
     fprintf(f, "charset = \"%s\"\n", cur->buf);
   }
+  fprintf(f, "advanced_layout\n");
 
   if (nbuiltin) {
     fprintf(f, "# The built-in variables are as follows\n");
@@ -2641,8 +2664,8 @@ generate_serve_cfg(FILE *f)
         "use_corr = 1\n"
         "corr_dir = \"%Ps\"\n"
         "real_time_limit = 5\n"
-        "check_cmd = \"check_%lPs\"\n"
-        "xml_file = \"%Ps.xml\"\n"
+        "check_cmd = \"check\"\n"
+        "xml_file = \"statement.xml\"\n"
         "max_vm_size = 64M\n"
         "max_stack_size = 64M\n"
         "max_file_size = 64M\n"
@@ -2852,24 +2875,12 @@ generate_contest_xml(FILE *f)
           "  <name>Test contest</name>\n"
           "  <name_en>Test contest</name_en>\n"
           "\n"
-          "  <register_access default=\"deny\">\n"
-          "    <ip allow=\"yes\">127.</ip>\n"
-          "  </register_access>\n"
-          "  <users_access default=\"deny\">\n"
-          "    <ip allow=\"yes\">127.</ip>\n"
-          "  </users_access>\n"
-          "  <team_access default=\"deny\">\n"
-          "    <ip allow=\"yes\">127.</ip>\n"
-          "  </team_access>\n"
-          "  <judge_access default=\"deny\">\n"
-          "    <ip allow=\"yes\">127.</ip>\n"
-          "  </judge_access>\n"
-          "  <master_access default=\"deny\">\n"
-          "    <ip allow=\"yes\">127.</ip>\n"
-          "  </master_access>\n"
-          "  <serve_control_access default=\"deny\">\n"
-          "    <ip allow=\"yes\">127.</ip>\n"
-          "  </serve_control_access>\n"
+          "  <register_access default=\"allow\" />\n"
+          "  <users_access default=\"allow\" />\n"
+          "  <team_access default=\"allow\" />\n"
+          "  <judge_access default=\"allow\" />\n"
+          "  <master_access default=\"allow\" />\n"
+          "  <serve_control_access default=\"allow\" />\n"
           "\n"
           "  <caps>\n"
           "    <cap login=\"%s\">MASTER_SET,</cap>\n"
@@ -3034,8 +3045,8 @@ generate_ejudge_xml(FILE *f)
   if (config_var_dir[0]) {
     fprintf(f, "  <var_dir>%s</var_dir>\n", config_var_dir);
     // FIXME: should make configurable paths?
-    fprintf(f, "  <userlist_log>userlist.log</userlist_log>\n");
-    fprintf(f, "  <super_serve_log>super_serve.log</super_serve_log>\n");
+    fprintf(f, "  <userlist_log>ej-users.log</userlist_log>\n");
+    fprintf(f, "  <super_serve_log>ej-super-server.log</super_serve_log>\n");
     //fprintf(f, "  <compile_log>%s</compile_log>\n");
   }
 
@@ -3230,7 +3241,7 @@ do_preview_menu(void)
   snprintf(script_dir, sizeof(script_dir), "%s/lang",
            tmp_work_dir);
   lang_configure_screen(script_dir, script_in_dirs, 0,
-                        tmp_work_dir, 0, 0, preview_header);
+                        tmp_work_dir, 0, 0, preview_header, 0);
 
   while (1) {
     mvwprintw(stdscr, 0, 0, "Ejudge %s configurator > File preview",
@@ -3409,77 +3420,8 @@ gen_check_retcode(FILE *f, const unsigne
 
 // solutions and tests for the sample contest 1
 const unsigned char b64_contest1_tgz[] =
-"begin-base64 664 contest-1.tar.gz\n"
-"H4sIAGOB+EcAA+1dW2/bRhb2a/grBtqkkBKZ4fCmupYd2IlRGNgGQZyiD93C\n"
-"IClaZkuRAkk5dooAuWC3BZrdxe7LAgss+rivbrbeppvE+QvUP9ozQ4qkYsdy\n"
-"UIlSrPO1Dsm5cEYcfmfOOTxDhr7bixzfC68vTAwSoNHQ2FaVlUZxO8AChRK6\n"
-"pOuarCxIlGq6tkC0yXUpRy+MjICQBWvXDs4qNyr/A0WYjb+x3bIssWuEY2+D\n"
-"DbCuq+8cf6pRGH9JUxuyQhUZxl9RJLpApLH35BTM+fh3A78dGB0S9jrLwh5c\n"
-"CaNOzDqxyCfE9b2240XLgmnDViCEBLbRcr2qUVsuHJnJEdRYIQa5Rkx+eD9w\n"
-"IhtyLci1vZYoTPuHIk5Fkf9Rd3b4r+jI/zKA/J9vFPn/tbFnWNuhJbKdMbYx\n"
-"iv8qlYH/GvBfA1EA6VRVGyryvww4na4fRISNuOj44tVloZgCt4YrblmG59nB\n"
-"siB0e6brWGTH8QyXWK4RhsTYNoVvgfBpFlzMCDZ7vtMiHcPxqltR4HhtYgTt\n"
-"8MuvaiTaDfz7IdnYt+wuu+2gJqtNSNoICckK8ez7g+Pq1kEY2R3R8RIpQ0Ag\n"
-"cREFvWGHBhQPRc/ejza9qJqWMU9LTE/k9yKxC12KmBxj0opnPxQezqmAKvLf\n"
-"BP3fmkAbI+d/XR/Yf2D3sflfbjD7D/k/efzO8Sy317JJM4xaIAB2VwWBcYyT\n"
-"l9G4xunNdIGcdyGQc6daueK24P9KnXwEOR8lTOLcSvL+4EFWzrHAjnqBR6Tl\n"
-"ueXaLKLI//a1a6LV7Y69jZH8b2T6vwxSgOn/VJWR/2Ug57/jhxGo9J1VoRey\n"
-"KdszOnbYNSwbJvXWci4UEoGQz8Og+jseWV0lRmE/SYfJljSbuVWAImDmMMR/\n"
-"6+txq/4cI/hPJZlm87+qM/+vouoN5H8ZOKn/n6blfwbEfw81P9fquUS5539j\n"
-"e84Dpt1HA/V+OKPK0tZ7Ozt2YLfu2kYrTdr0ur0oKZum5vZA7XSLIIzEwA7t\n"
-"aOvAi4z9gfYPqbbvboZbTttzdhzQX6LqjuGGdp5/3w9aN3eNIKwqSp3ImlbI\n"
-"2nWiRBYmBaQ6UeRa2mAUHKS/lpdlZgf/XdA0N0/ABrHbdiB2oabNDBIoFO4Z\n"
-"bnr6k5XM81Q605wBg4ZYRmTtkmpmaZH9WtbNtLK970RVOqgx12bQ3KIo/ztW\n"
-"KFrjd/+O1P8USWHyX29osq5KDa7/of+nHCS6XiIQQKDl4v4m2wOZf6ko61kG\n"
-"iCIm67/8ikv7mnAJylziQjiRW4os3mFSq3oT7irftUUmuX/veHaVSeykqHm+\n"
-"ooPkL5gzmadXvpUeFqzKSyixfiOK/O8eRLu+J3YPxtzGKP5L3P8jaZqk6rLC\n"
-"9b+GivwvBYy0wMhqYNzfdpi2BdQTzNMSuZqRMA8pd2FQ5H/QMw/EwBx7G5zi\n"
-"6ln8V7L4H1WR+PNf9P+WA8b/th2B4rfrd7qc+YVDoH5IDDHytx3Ge76D5L9I\n"
-"GPL/NBrijj/+u3yk/1eR0/lf03XK+U819P+UgsQhsHn73sanG3evqsSom3Ur\n"
-"dRPc3Vi7RapX61dr3MGSplokjfNIj7+4u3lvIy02qLlx+xaKiQ8CQ/b/gxDu\n"
-"8o4thlZnnG2M1P/VxuD5T0NjegJVVYz/LAfVlhN2XeOAVK+RKovoqqUb0PiZ\n"
-"C9YFq7smIJkvKoaf/0wr/kNeYAsEFFmS5UYS/6Fh/GcpOGf8x9BDlkH4R+vK\n"
-"u4I/MPbjQ8EQ/2co/lvG+b8UYPz3fGPI/7/bFeFv7G2M8v9RJYv/1GVNT/x/\n"
-"OP+XguYNwXXCiFQvGzUw7HeSuX3r3q3N23UCE3kF+JsWMN9ZIHk0cJmx/7JJ\n"
-"RFKB6X9ZuLGKpJ95DPHfDlyx6469jZH2fxb/qYAtoHH+q+j/KwXczw/kB2o3\n"
-"OalXgc9pojmUOETyZaT2xcDQ+o9udzrx39z/V1z/rygaPv8vBSfjv5kL4K1g\n"
-"78LqD4z2vlgo8v/AMI3QsURzzD6AkfO/JmX+f0Xj6z+pLiH/y8Dm7Tuf3yOV\n"
-"CjGEbNcU8md8d+5u3r5HLCTwBUXOf3Nq/n9ZUjP/P1P8uf8f9f9S8B7+f7Y0\n"
-"W6qTff4M4P6u49qkmj0KYA8C9mtkZYXQWrJsglxbYWVPPBUI8YnADOGU9z9E\n"
-"Jb//gYIAyN7/IMuUzf8aPv8vB+db/2Vsq7j8C5d/IS4civJ/z/QscW9C8d9n\n"
-"6X9UzdZ/0WT9h6Ki/C8Fn/mtHihyhils9cxkfVdNuOV0sufARjiQRgITZydX\n"
-"avGY8VOScxvy5DIuqyZseC0CTfJt0gmUPVPAUPzHDqgCgeGNOwj8/eO/VaWB\n"
-"6z9KAcZ/zzeK/N+Zpfgv9P+WAoz/mm8Mx38Gvuu3waL0x9rG6PhvZbD+W6WU\n"
-"r/+Q8f2/5eAKWby6SJKBZ7vCJ4vE8ZzIMVzngcHujCrz8NREgW0I5DLab3u9\n"
-"jmkH1bVafeh4HY5vEickayAI1uuJFKjehFTPrZNdw41EXEsyU2CuPLtje9EE\n"
-"PwDBQ7zO9f2HBnMAsO8/NKiM338oA4XxXxf3O+MP/lsY7f/XVR7/y4L+wPxj\n"
-"8X+yjvH/5aB5Awad7NlBCKJ+pUJFqUJsz/JbjtdeqfSincWPK+TGqtCEKcJ0\n"
-"7Q7T7bqG9Y3RtsEOrAQ90f6612rbYmh0uq69bfleZIdRhZVzWqzIOt+PDrq8\n"
-"Alxtr2UErcoqpDazu4+4htfuwUlX4JTbdz/n2VAgciLXXo3/ER/GP8eH/e/i\n"
-"Q7LevJ6kJiVadmgFDvdxs16uxv+CMv3H/SdQ5TWv9Aj2X8fH8SsSv4mP4eA4\n"
-"/l98ROLn8N8xK0HiX9Lz/63/BKr+NSn4OH4ZH0HyMZQ7hGpH8cv+M3Ymdvb+\n"
-"M9L/E0/6of9HAh17AeXhUBTif8LJ/svrHQ0yXsbH5zhn/ILALvTsFaS/7j/r\n"
-"fw8Hi3JKE5Jl/wTVX6bZg1xo9++8B/85V1Os+4/TVo+hi0+Kp4aivOdH7JK9\n"
-"iH8hqjT8C388x+n5NX0eH8Hl/x72jrIr23/EzgklD2EY+KiwzWu4moeFMYFW\n"
-"Th/I/g/xr4WBhCqQkg0kO+sbaOw73qfD+FfWM3aWp3BRX/Wfkv6f2XV+a8ia\n"
-"17vp3XR96HZiCdktyg/tfX6bh2nx9HA1dXs0+ctyVimRiULU5vXkcJDp9yKe\n"
-"KzWvp7tpm4WzZAfQAvQq4dwk4+gL8n9tWvJfy+I/B/M/yH8F5X8ZmLz8Xxuz\n"
-"/F+boPz/y0BK/cyk2ikS/mV8WD8hpF/ER8KiIr9DRkMu4ZnlizS4HEL870HH\n"
-"T/mZb9hkwdrh1wfOFT/n/QMZzS+SSOIf+f5PA/kOPWONw696xbsIV4fw7j1i\n"
-"s0FhvuNzR2EmEth1SWanY978U/7vE/ixT1mHeFNPeeNJE1kvXrBeZaN19K76\n"
-"Y5figvwOCa7MkgD/jWBkneS3/xjOY/8N4v8oZfo/+xAE2n9lIBn/tYneAee3\n"
-"//UGfxcEpSwkHMe/BAzGX5I00fAm8PLfhXPE/0vqW+MvSxra/6VAayilXGbE\n"
-"bCLnvzJF/mfv/1FUvv5XlvD9v+VAml3VFFECcv7LM8J/yvmv4PqfUqAh/+ca\n"
-"Of/pbPCfv/8H5n/8/lspkJH/c43i/N8yoom0wfl/1vv/uf2ffP9RlxL+4/P/\n"
-"ciALCkqAOUbR/p8e/7Wc/7LE+U9R/y8Fi6qgogCYX+T8V2dD/29Q1P9LxMdI\n"
-"/rlG0f6fhflf01P9H9d/lAIqUFyRMccozv8zYf/LDeR/iUD1f75RjP+ZKP/P\n"
-"0v+pnMf/yGry/A/f/1AKqKxQQVWXllAKzCUS/q/PQPwnf/+fDAWT+E+K8Z9l\n"
-"YDD+U43/ojQff01D/0+JWFrSNB2uPIr/+UTO/2nGf2Xvf1Y0JfX/4vt/SsHS\n"
-"ko7Un2Pk/J9m/JeS819O53+M/ygFFANA5xrF+X+S/h/1LP+vrOTrP7XU/4vx\n"
-"H6WASpIgw58Cfyr8LaI7eK5QtP+nxX+qFOx/KfH/ymj/lwKaQqAyx2ALO0sJ\n"
-"TuzkCVnK0tCWbVCKfBgo6v9Tm/+pfHL+x/WfpYAKLAJcFdAMQCAQCAQCgUAg\n"
-"EAgEAoFAIC4e/g/ULtleAMgAAA==\n"
-"====\n";
+#include "contest-1/contest-1.c"
+  ;
 
 static void
 generate_install_script(FILE *f)
@@ -3523,6 +3465,23 @@ generate_install_script(FILE *f)
   fprintf(f, "# Generated by ejudge-setup, version %s\n", compile_version);
   fprintf(f, "# Generation date: %s\n\n", date_buf);
 
+  fprintf(f,
+          "echo \"This script will ERASE the exising user database and contests!\"\n"
+          "echo \"Are you sure to proseed (yes/no)?\"\n"
+          "read confirm\n"
+          "if [ \"${confirm}\" != \"yes\" ];\n"
+          "then\n"
+          "  echo \"Operation is not confirmed\"\n"
+          "  exit 0\n"
+          "fi\n"
+          "uid=`id -u`\n"
+          "if [ \"${uid}\" != 0 ]\n"
+          "then\n"
+          "  echo \"This script must be run by ROOT user\" >&2\n"
+          "  exit 1\n"
+          "fi\n"
+          "");
+
   // create all the necessary directories
   fprintf(f, "# create all necessary directories\n");
   generate_dir_creation(f, &created_dirs, 0, config_ejudge_contests_home_dir);
@@ -3910,7 +3869,7 @@ preview_install_script(void)
   script_in_dirs[1] = 0;
   snprintf(script_dir, sizeof(script_dir), "%s/lang", tmp_work_dir);
   lang_configure_screen(script_dir, script_in_dirs, 0,
-                        tmp_work_dir, 0, 0, header);
+                        tmp_work_dir, 0, 0, header, 0);
 
   /*
   snprintf(script_dir, sizeof(script_dir), "%s/lang",
@@ -3928,7 +3887,7 @@ preview_install_script(void)
 }
 
 static void
-save_install_script(void)
+save_install_script(int batch_mode, const unsigned char *output_name)
 {
   FILE *f = 0;
   char *txt_ptr = 0, *p;
@@ -3948,7 +3907,7 @@ save_install_script(void)
   script_in_dirs[1] = 0;
   snprintf(script_dir, sizeof(script_dir), "%s/lang", tmp_work_dir);
   lang_configure_screen(script_dir, script_in_dirs, 0, tmp_work_dir,
-                        0, 0, header);
+                        0, 0, header, batch_mode);
 
   if (check_install_script_validity() < 0) return;
 
@@ -3956,11 +3915,18 @@ save_install_script(void)
   generate_install_script(f);
   close_memstream(f); f = 0;
 
-  snprintf(filepath, sizeof(filepath), "ejudge-install.sh");
-  j = ncurses_edit_string(LINES/2, COLS, "Setup script name",
-                          filepath, sizeof(filepath), utf8_mode);
-  if (j < 0) {
-    goto cleanup;
+  if (output_name && *output_name) {
+    snprintf(filepath, sizeof(filepath), "%s", output_name);
+  } else {
+    snprintf(filepath, sizeof(filepath), "ejudge-install.sh");
+  }
+
+  if (!batch_mode) {
+    j = ncurses_edit_string(LINES/2, COLS, "Setup script name",
+                            filepath, sizeof(filepath), utf8_mode);
+    if (j < 0) {
+      goto cleanup;
+    }
   }
 
   if ((fd = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0770)) < 0) {
@@ -4062,7 +4028,7 @@ do_main_menu(void)
       preview_install_script();
       break;
     case 6:
-      save_install_script();
+      save_install_script(0, NULL);
       break;
     }
   }
@@ -4187,6 +4153,7 @@ main(int argc, char **argv)
   int answer = 1;
   int cur_arg = 1;
   const unsigned char *user = 0, *group = 0, *workdir = 0;
+  int batch_mode = 0;
 
   while (cur_arg < argc) {
     if (!strcmp(argv[cur_arg], "-u")) {
@@ -4201,6 +4168,9 @@ main(int argc, char **argv)
       if (cur_arg + 1 >= argc) arg_expected(argv[0]);
       workdir = argv[cur_arg + 1];
       cur_arg += 2;
+    } else if (!strcmp(argv[cur_arg], "-b")) {
+      batch_mode = 1;
+      cur_arg += 1;
     } else {
       break;
     }
@@ -4228,9 +4198,19 @@ main(int argc, char **argv)
            EJUDGE_SERVER_BIN_PATH);
   initialize_config_vars();
   initialize_setting_vars();
-  //answer = ncurses_yesno(0, initial_warning);
-  if (answer == 1) {
-    do_main_menu();
+
+  if (batch_mode) {
+    snprintf(config_user_id, sizeof(config_user_id), "%d", 1);
+    snprintf(config_login, sizeof(config_login), "%s", "ejudge");
+    snprintf(config_email, sizeof(config_email), "%s", "ejudge@localhost");
+    snprintf(config_name, sizeof(config_name), "%s", "ejudge administrator");
+    make_sha1_passwd(config_password_sha1, "ejudge");
+    save_install_script(1, "ejudge-install.sh");
+  } else {
+    //answer = ncurses_yesno(0, initial_warning);
+    if (answer == 1) {
+      do_main_menu();
+    }
   }
 
   ncurses_shutdown();
diff -ruNp ejudge-2.3.18/extra/ChangeLog ejudge/extra/ChangeLog
--- ejudge-2.3.18/extra/ChangeLog	2011-04-10 09:25:19.000000000 +0400
+++ ejudge/extra/ChangeLog	2011-07-11 09:02:01.872322476 +0400
@@ -1,3 +1,9 @@
+2011-05-02 (r6293) Alexander Chernov  <cher@ejudge.ru>
+
+	* Makefile.in: copyright notice is updated;
+	rules for compilation of explicitly 32- and 64-bit versions
+	of libdropcaps.so are added.
+
 2010-01-03 (r5602) Alexander Chernov  <cher@ejudge.ru>
 
 	* Makefile.in: copyright notice is updated;
diff -ruNp ejudge-2.3.18/extra/libdropcaps.c ejudge/extra/libdropcaps.c
--- ejudge-2.3.18/extra/libdropcaps.c	2011-04-10 09:25:19.000000000 +0400
+++ ejudge/extra/libdropcaps.c	2011-07-11 09:02:01.870322512 +0400
@@ -1,7 +1,7 @@
 /* -*- mode: c -*- */
-/* $Id: libdropcaps.c 5512 2008-12-27 19:32:44Z cher $ */
+/* $Id: libdropcaps.c 6383 2011-06-26 20:22:33Z cher $ */
 
-/* Copyright (C) 2003-2008 Alexander Chernov <cher@ejudge.ru> */
+/* Copyright (C) 2003-2011 Alexander Chernov <cher@ejudge.ru> */
 
 /*
  * This program is free software; you can redistribute it and/or modify
@@ -48,11 +48,13 @@ init(void)
   if (ptrace(0x4281, 0, 0, 0) >= 0) {
     // new secure exec interface
     /* if getppid works, CAP_SYS_OPERATIONS has no effect :-( */
+    /*
     if (dup(0) >= 0) {
       fprintf(stderr,
               "capexec: CAP_SYS_OPERATIONS is not supported on this system\n");
       _exit(6);
     }
+    */
     return;
   }
 
diff -ruNp ejudge-2.3.18/extra/Makefile.in ejudge/extra/Makefile.in
--- ejudge-2.3.18/extra/Makefile.in	2011-04-10 09:25:19.000000000 +0400
+++ ejudge/extra/Makefile.in	2011-07-11 09:02:01.868322548 +0400
@@ -1,7 +1,7 @@
 # -*- Makefile -*-
-# $Id: Makefile.in 5685 2010-01-19 10:08:31Z cher $
+# $Id: Makefile.in 6293 2011-05-02 16:42:58Z cher $
 
-# Copyright (C) 2002-2007 Alexander Chernov <cher@ejudge.ru> */
+# Copyright (C) 2002-2011 Alexander Chernov <cher@ejudge.ru> */
 
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -48,6 +48,8 @@ LIBCAP_LIB_OPT=@ac_cv_libcap_lib_opt@
 LIBCAP_LINK=@ac_cv_libcap_link@
 
 WERROR=@ac_cv_werror_flag@
+NEED32=@ac_cv_need_32@
+NEED64=@ac_cv_need_64@
 
 ifdef RELEASE
 CDEBUGFLAGS=-O2 -s -Wall -DNDEBUG -DRELEASE ${WERROR}
@@ -70,6 +72,12 @@ ifndef NO_KERNEL
 TARGETS += capexec
 ifndef STATIC
 TARGETS += libdropcaps.so
+ifdef NEED32
+TARGETS += libdropcaps32.so
+endif
+ifdef NEED64
+TARGETS += libdropcaps64.so
+endif
 endif
 endif
 
@@ -83,6 +91,8 @@ install:
 	for i in ${TARGETALLWAYS}; do install -m 755 $$i "${DESTDIR}${libexecdir}/ejudge/lang"; done
 	if [ x"${NO_KERNEL}" = x ]; then install -m 755 capexec "${DESTDIR}${libexecdir}/ejudge/lang"; fi
 	if [ x"${NO_KERNEL}" = x -a x"${STATIC}" = x ]; then install -m 755 libdropcaps.so "${DESTDIR}${libexecdir}/ejudge/lang"; fi
+	if [ x"${NO_KERNEL}" = x -a x"${STATIC}" = x -a x"${NEED32}" != x ]; then install -m 755 libdropcaps32.so "${DESTDIR}${libexecdir}/ejudge/lang"; fi
+	if [ x"${NO_KERNEL}" = x -a x"${STATIC}" = x -a x"${NEED64}" != x ]; then install -m 755 libdropcaps64.so "${DESTDIR}${libexecdir}/ejudge/lang"; fi
 
 bpcemu2: bpcemu2.o
 	${LD} ${LDFLAGS} $^ -o $@ ${LDLIBS}
@@ -109,6 +119,16 @@ libdropcaps.so : libdropcaps.o
 libdropcaps.o : libdropcaps.c
 	${CC} ${CFLAGS} -DPIC -fPIC -c libdropcaps.c -o libdropcaps.o
 
+libdropcaps32.so : libdropcaps32.o
+	${LD} -shared ${LDFLAGS} $^ -m32 -o $@ ${LIBCAP_LINK} ${LDLIBS}
+libdropcaps32.o : libdropcaps.c
+	${CC} ${CFLAGS} -DPIC -fPIC -m32 -c libdropcaps.c -o libdropcaps32.o
+
+libdropcaps64.so : libdropcaps64.o
+	${LD} -shared ${LDFLAGS} $^ -m64 -o $@ ${LIBCAP_LINK} ${LDLIBS}
+libdropcaps64.o : libdropcaps.c
+	${CC} ${CFLAGS} -DPIC -fPIC -m64 -c libdropcaps.c -o libdropcaps64.o
+
 dosrun3: dosrun3.o
 	${LD} ${LDFLAGS} $^ -o $@ ${LDLIBS}
 dosrun3.o: dosrun3.c
diff -ruNp ejudge-2.3.18/files.make ejudge/files.make
--- ejudge-2.3.18/files.make	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/files.make	2011-07-11 09:02:04.531273619 +0400
@@ -1,5 +1,5 @@
 # -*- Makefile -*-
-# $Id: files.make 6185 2011-03-27 15:16:29Z cher $
+# $Id: files.make 6356 2011-06-05 07:47:18Z cher $
 
 # Copyright (C) 2002-2011 Alexander Chernov <cher@ejudge.ru> */
 
@@ -23,6 +23,7 @@ USERLIST_CLNT_CFILES=\
  userlist_clnt/copy_user_info.c\
  userlist_clnt/create_member.c\
  userlist_clnt/create_user.c\
+ userlist_clnt/create_user_2.c\
  userlist_clnt/delete_cookie.c\
  userlist_clnt/delete_field.c\
  userlist_clnt/delete_info.c\
@@ -33,16 +34,19 @@ USERLIST_CLNT_CFILES=\
  userlist_clnt/generate_team_passwd.c\
  userlist_clnt/get_contests.c\
  userlist_clnt/get_cookie.c\
+ userlist_clnt/get_count.c\
  userlist_clnt/get_database.c\
  userlist_clnt/get_fd.c\
  userlist_clnt/get_info.c\
  userlist_clnt/get_param.c\
+ userlist_clnt/get_prev_user_id.c\
  userlist_clnt/get_uid_by_pid.c\
  userlist_clnt/get_uid_by_pid_2.c\
  userlist_clnt/get_xml_by_text.c\
  userlist_clnt/import_csv_users.c\
  userlist_clnt/list_all_users.c\
  userlist_clnt/list_users.c\
+ userlist_clnt/list_users_2.c\
  userlist_clnt/login.c\
  userlist_clnt/logout.c\
  userlist_clnt/lookup_cookie.c\
@@ -116,6 +120,7 @@ COMMON_CFILES=\
  allowed_list.c\
  archive_paths.c\
  base64.c\
+ bitset.c\
  cgi.c\
  charsets.c\
  clarlog.c\
@@ -212,6 +217,7 @@ COMMON_CFILES=\
  super_html_3.c\
  super_html_4.c\
  super_html_5.c\
+ super_html_6.c\
  super_proto.c\
  sha.c\
  t3m_dir_listener.c\
@@ -462,6 +468,7 @@ CFILES=\
 HFILES=\
  archive_paths.h\
  base64.h\
+ bitset.h\
  cgi.h\
  charsets.h\
  clarlog.h\
diff -ruNp ejudge-2.3.18/html.c ejudge/html.c
--- ejudge-2.3.18/html.c	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/html.c	2011-07-11 09:02:04.346277022 +0400
@@ -1,5 +1,5 @@
 /* -*- mode: c -*- */
-/* $Id: html.c 6216 2011-04-02 06:32:30Z cher $ */
+/* $Id: html.c 6343 2011-05-22 21:20:22Z cher $ */
 
 /* Copyright (C) 2000-2011 Alexander Chernov <cher@ejudge.ru> */
 
@@ -1219,6 +1219,7 @@ do_write_kirov_standings(
         const unsigned char *stand_dir,
         int client_flag,
         int only_table_flag,
+        int user_id,
         const unsigned char *header_str,
         unsigned char const *footer_str,
         int raw_flag,
@@ -1509,8 +1510,18 @@ do_write_kirov_standings(
     prob = state->probs[pe->prob_id];
     if (!prob || tind < 0 || pind < 0 || prob->hidden) continue;
 
+    /*
+      if (client_flag != 1 || user_id) {
+        if (run_time < start_time) run_time = start_time;
+        if (current_dur > 0 && run_time - start_time > current_dur) continue;
+        if (global->stand_ignore_after > 0
+            && pe->time >= global->stand_ignore_after)
+          continue;
+      }
+     */
+
     // ignore future runs when not in privileged mode
-    if (!client_flag) {
+    if (!client_flag || user_id > 0) {
       run_time = pe->time;
       if (run_time < start_time) run_time = start_time;
       if (stop_time && run_time > stop_time) run_time = stop_time;
@@ -4308,7 +4319,7 @@ write_standings(
   }
   if (global->score_system == SCORE_KIROV
       || global->score_system == SCORE_OLYMPIAD)
-    do_write_kirov_standings(state, cnts, f, stat_dir, 0, 0, header_str,
+    do_write_kirov_standings(state, cnts, f, stat_dir, 0, 0, 0, header_str,
                              footer_str, 0, accepting_mode, force_fancy_style,
                              0, charset_id, NULL, user_mode);
   else if (global->score_system == SCORE_MOSCOW)
@@ -4657,6 +4668,13 @@ done:
   return 0;
 }
 
+static const unsigned char *
+html_make_title(unsigned char *buf, size_t size, const unsigned char *title)
+{
+  snprintf(buf, size, " title=\"%s\"", title);
+  return buf;
+}
+
 int
 write_xml_team_testing_report(
         const serve_state_t state,
@@ -4665,7 +4683,11 @@ write_xml_team_testing_report(
         int output_only,
         int is_marked,
         const unsigned char *txt,
-        const unsigned char *table_class)
+        const unsigned char *table_class,
+        ej_cookie_t sid,
+        const unsigned char *self_url,
+        const unsigned char *extra_args,
+        const int *action_vec)
 {
   const struct section_global_data *global = state->global;
   testing_report_xml_t r = 0;
@@ -4674,9 +4696,11 @@ write_xml_team_testing_report(
   int need_comment = 0, need_info = 0, is_kirov = 0, i;
   unsigned char cl[128] = { 0 };
   struct html_armor_buffer ab = HTML_ARMOR_INITIALIZER;
-  int visibility = 0, serial = 0, has_full = 0;
+  int visibility = 0, serial = 0, has_full = 0, need_links = 0;
   int status, score, max_score;
   int run_tests, tests_passed;
+  unsigned char hbuf[1024];
+  unsigned char tbuf[1024];
 
   if (table_class && *table_class) {
     snprintf(cl, sizeof(cl), " class=\"%s\"", table_class);
@@ -4791,6 +4815,7 @@ write_xml_team_testing_report(
     if (visibility == TV_FULL) {
       if (t->status == RUN_RUN_TIME_ERR) need_info = 1;
       has_full = 1;
+      if (r->archive_available) need_links = 1;
     }
   }
 
@@ -4808,6 +4833,9 @@ write_xml_team_testing_report(
   if (need_comment) {
     fprintf(f, "<th%s>%s</th>", cl, _("Comment"));
   }
+  if (need_links) {
+    fprintf(f, "<th%s>%s</th>", cl, _("Links"));
+  }
 
   fprintf(f, "</tr>\n");
 
@@ -4892,6 +4920,52 @@ write_xml_team_testing_report(
         xfree(s);
       }
     }
+    if (need_links) {
+      fprintf(f, "<td%s>", cl);
+      if (visibility == TV_FULL) {
+        fprintf(f, "&nbsp;%s[I]</a>",
+                html_hyperref_attr(hbuf, sizeof(hbuf), sid, self_url, extra_args,
+                                   html_make_title(tbuf, sizeof(tbuf), _("Test input data")),
+                                   "action=%d&run_id=%d&test_num=%d",
+                                   action_vec[0], r->run_id, t->num));
+        if (t->output_available) {
+          fprintf(f, "&nbsp;%s[O]</a>",
+                  html_hyperref_attr(hbuf, sizeof(hbuf), sid, self_url, extra_args,
+                                     html_make_title(tbuf, sizeof(tbuf), _("Program output")),
+                                     "action=%d&run_id=%d&test_num=%d",
+                                     action_vec[1], r->run_id, t->num));
+        }
+        if (r->correct_available) {
+          fprintf(f, "&nbsp;%s[A]</a>",
+                  html_hyperref_attr(hbuf, sizeof(hbuf), sid, self_url, extra_args,
+                                     html_make_title(tbuf, sizeof(tbuf), _("Correct answer")),
+                                     "action=%d&run_id=%d&test_num=%d",
+                                     action_vec[2], r->run_id, t->num));
+        }
+        if (t->stderr_available) {
+          fprintf(f, "&nbsp;%s[E]</a>",
+                  html_hyperref_attr(hbuf, sizeof(hbuf), sid, self_url, extra_args,
+                                     html_make_title(tbuf, sizeof(tbuf), _("Program stderr output")),
+                                     "action=%d&run_id=%d&test_num=%d",
+                                     action_vec[3], r->run_id, t->num));
+        }
+        if (t->checker_output_available) {
+          fprintf(f, "&nbsp;%s[C]</a>",
+                  html_hyperref_attr(hbuf, sizeof(hbuf), sid, self_url, extra_args,
+                                     html_make_title(tbuf, sizeof(tbuf), _("Checker output")),
+                                     "action=%d&run_id=%d&test_num=%d",
+                                     action_vec[4], r->run_id, t->num));
+        }
+        if (r->info_available) {
+          fprintf(f, "&nbsp;%s[F]</a>",
+                  html_hyperref_attr(hbuf, sizeof(hbuf), sid, self_url, extra_args,
+                                     html_make_title(tbuf, sizeof(tbuf), _("Test information")),
+                                     "action=%d&run_id=%d&test_num=%d",
+                                     action_vec[5], r->run_id, t->num));
+        }
+      }
+      fprintf(f, "</td>");
+    }
     fprintf(f, "</tr>\n");
   }
   fprintf(f, "</table>\n");
diff -ruNp ejudge-2.3.18/html.h ejudge/html.h
--- ejudge-2.3.18/html.h	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/html.h	2011-07-11 09:02:04.353276893 +0400
@@ -1,5 +1,5 @@
 /* -*- c -*- */
-/* $Id: html.h 6111 2011-03-14 20:55:17Z cher $ */
+/* $Id: html.h 6343 2011-05-22 21:20:22Z cher $ */
 #ifndef __HTML_H__
 #define __HTML_H__
 
@@ -84,7 +84,9 @@ do_write_kirov_standings(
         const struct contest_desc *cnts,
         FILE *f,
         const unsigned char *stand_dir,
-        int client_flag, int only_table_flag,
+        int client_flag,
+        int only_table_flag,
+        int user_id,
         const unsigned char *header_str,
         unsigned char const *footer_str,
         int raw_flag,
@@ -188,7 +190,11 @@ write_xml_team_testing_report(
         int output_only,
         int is_marked,
         const unsigned char *txt,
-        const unsigned char *table_class);
+        const unsigned char *table_class,
+        ej_cookie_t sid,
+        const unsigned char *self_url,
+        const unsigned char *extra_args,
+        const int *action_vec);
 
 int
 write_xml_team_tests_report(
diff -ruNp ejudge-2.3.18/html_hyperref.c ejudge/html_hyperref.c
--- ejudge-2.3.18/html_hyperref.c	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/html_hyperref.c	2011-07-11 09:02:04.181280054 +0400
@@ -1,7 +1,7 @@
 /* -*- mode: c -*- */
-/* $Id: html_hyperref.c 5675 2010-01-19 09:52:11Z cher $ */
+/* $Id: html_hyperref.c 6343 2011-05-22 21:20:22Z cher $ */
 
-/* Copyright (C) 2005-2006 Alexander Chernov <cher@ejudge.ru> */
+/* Copyright (C) 2005-2011 Alexander Chernov <cher@ejudge.ru> */
 
 /*
  * This program is free software; you can redistribute it and/or modify
@@ -51,9 +51,44 @@ html_hyperref(unsigned char *buf, size_t
   return buf;
 }
 
+unsigned char *
+html_hyperref_attr(
+        unsigned char *buf,
+        size_t size,
+        ej_cookie_t session_id,
+        const unsigned char *self_url,
+        const unsigned char *extra_args,
+        const unsigned char *attrs,
+        const char *format,
+        ...)
+{
+  unsigned char b[1024] = { 0 };
+  va_list args;
+
+  if (!attrs) attrs = "";
+  if (format && *format) {
+    va_start(args, format);
+    vsnprintf(b, sizeof(b), format, args);
+    va_end(args);
+  }
+
+  if (extra_args && *extra_args && *b) {
+    snprintf(buf, size, "<a%s href=\"%s?SID=%016llx&%s&%s\">",
+             attrs, self_url, session_id, extra_args, b);
+  } else if (extra_args && *extra_args) {
+    snprintf(buf, size, "<a%s href=\"%s?SID=%016llx&%s\">",
+             attrs, self_url, session_id, extra_args);
+  } else if (*b) {
+    snprintf(buf, size, "<a%s href=\"%s?SID=%016llx&%s\">", attrs, self_url, session_id, b);
+  } else {
+    snprintf(buf, size, "<a%s href=\"%s?SID=%016llx\">", attrs, self_url, session_id);
+  }
+
+  return buf;
+}
+
 /*
  * Local variables:
  *  compile-command: "make"
- *  c-font-lock-extra-types: ("\\sw+_t" "FILE" "va_list" "fd_set" "DIR")
  * End:
  */
diff -ruNp ejudge-2.3.18/html_input_text.c ejudge/html_input_text.c
--- ejudge-2.3.18/html_input_text.c	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/html_input_text.c	2011-07-11 09:02:04.666271137 +0400
@@ -1,7 +1,7 @@
 /* -*- mode: c -*- */
-/* $Id: html_input_text.c 5675 2010-01-19 09:52:11Z cher $ */
+/* $Id: html_input_text.c 6323 2011-05-08 20:39:15Z cher $ */
 
-/* Copyright (C) 2006-2007 Alexander Chernov <cher@ejudge.ru> */
+/* Copyright (C) 2006-2011 Alexander Chernov <cher@ejudge.ru> */
 
 /*
  * This program is free software; you can redistribute it and/or modify
@@ -48,6 +48,40 @@ html_input_text(unsigned char *buf, size
   return buf;
 }
 
+unsigned char *
+html_input_text_js(
+        unsigned char *buf,
+        size_t size,
+        const unsigned char *var_name,
+        int text_size,
+        const unsigned char *onchange,
+        const char *format,
+        ...)
+{
+  va_list args;
+  unsigned char bformat[1024] = { 0 };
+  unsigned char bsize[128];
+  unsigned char bname[128];
+  unsigned char bonchange[128];
+
+  if (format && *format) {
+    va_start(args, format);
+    vsnprintf(bformat, sizeof(bformat), format, args);
+    va_end(args);
+  }
+
+  bsize[0] = 0;
+  if (text_size > 0) snprintf(bsize, sizeof(bsize), " size=\"%d\"", text_size);
+  bname[0] = 0;
+  if (var_name) snprintf(bname, sizeof(bname), " name=\"%s\"", var_name);
+  bonchange[0] = 0;
+  if (onchange) snprintf(bonchange, sizeof(bonchange), " onchange=\"%s\"", onchange);
+
+  snprintf(buf, size, "<input type=\"text\"%s%s%s value=\"%s\"/>",
+           bsize, bname, bonchange, bformat);
+  return buf;
+}
+
 /*
  * Local variables:
  *  compile-command: "make"
diff -ruNp ejudge-2.3.18/job-server.c ejudge/job-server.c
--- ejudge-2.3.18/job-server.c	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/job-server.c	2011-07-11 09:02:04.406275917 +0400
@@ -1,5 +1,5 @@
 /* -*- mode: c -*- */
-/* $Id: job-server.c 6162 2011-03-27 07:07:27Z cher $ */
+/* $Id: job-server.c 6347 2011-05-23 19:06:12Z cher $ */
 
 /* Copyright (C) 2006-2011 Alexander Chernov <cher@ejudge.ru> */
 
@@ -25,6 +25,7 @@
 #include "fileutl.h"
 #include "startstop.h"
 #include "compat.h"
+#include "misctext.h"
 
 #include "reuse_xalloc.h"
 #include "reuse_logger.h"
@@ -76,7 +77,7 @@ prepare_config_vars(void)
         return -1;
     }
   } else {
-    if (make_path_in_var_dir(job_server_log_path, "job_server.log") < 0)
+    if (make_path_in_var_dir(job_server_log_path, "ej-jobs.log") < 0)
       return -1;
   }
 
@@ -450,6 +451,23 @@ handle_mail_packet(int uid, int argc, ch
   if (argv[1][0]) charset = argv[1];
   if (!charset) charset = EJUDGE_CHARSET;
 
+  if (!argv[3] || !*argv[3]) {
+    err("mail: source email address is empty");
+    goto cleanup;
+  }
+  if (!is_valid_email_address(argv[3])) {
+    err("mail: source email address is invalid");
+    goto cleanup;
+  }
+  if (!argv[4] || !*argv[4]) {
+    err("mail: destination email address is empty");
+    goto cleanup;
+  }
+  if (!is_valid_email_address(argv[4])) {
+    err("mail: destination email address is invalid");
+    goto cleanup;
+  }
+
   cur_time = time(0);
   ptm = localtime(&cur_time);
   strftime(date_buf, sizeof(date_buf), "%a, %d %b %Y %H:%M:%S %z", ptm);
diff -ruNp ejudge-2.3.18/lang_config_vis.c ejudge/lang_config_vis.c
--- ejudge-2.3.18/lang_config_vis.c	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/lang_config_vis.c	2011-07-11 09:02:04.381276378 +0400
@@ -1,5 +1,5 @@
 /* -*- mode: c -*- */
-/* $Id: lang_config_vis.c 6146 2011-03-26 10:47:14Z cher $ */
+/* $Id: lang_config_vis.c 6387 2011-06-30 05:23:15Z cher $ */
 
 /* Copyright (C) 2008-2011 Alexander Chernov <cher@ejudge.ru> */
 
@@ -672,7 +672,8 @@ lang_configure_screen(
         const unsigned char *working_dir,
         unsigned char **keys,
         unsigned char **values,
-        const unsigned char *header)
+        const unsigned char *header,
+        int batch_mode)
 {
   WINDOW *in_win = 0, *out_win = 0;
   PANEL *in_pan = 0, *out_pan = 0;
@@ -705,9 +706,12 @@ lang_configure_screen(
 
   reconfigure_all_languages(script_dir, script_in_dirs, config_dir,
                             working_dir, keys, values, 0, in_win);
-  ncurses_print_help("Press any key");
-  doupdate();
-  c = getch();
+
+  if (!batch_mode) {
+    ncurses_print_help("Press any key");
+    doupdate();
+    c = getch();
+  }
 
   if (in_pan) del_panel(in_pan);
   if (out_pan) del_panel(out_pan);
@@ -762,7 +766,7 @@ lang_config_menu(
   unsigned char lang_id_buf[32];
 
   lang_configure_screen(script_dir, script_in_dirs, 0,
-                        working_dir, 0, 0, header);
+                        working_dir, 0, 0, header, 0);
   assign_lang_ids();
 
   for (pcfg = lang_first; pcfg; pcfg = pcfg->next) {
@@ -1007,6 +1011,9 @@ lang_config_generate_compile_cfg(
     if ((s = shellconfig_get(p->cfg, "secure"))) {
       fprintf(f, "disable_security\n");
     }
+    if ((s = shellconfig_get(p->cfg, "binary"))) {
+      fprintf(f, "binary\n");
+    }
     if (!(s = shellconfig_get(p->cfg, "cmd"))) s = p->lang;
     fprintf(f, "cmd = \"%s\"\n", s);
     if ((s = shellconfig_get(p->cfg, "arch"))) {
diff -ruNp ejudge-2.3.18/lang_config_vis.h ejudge/lang_config_vis.h
--- ejudge-2.3.18/lang_config_vis.h	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/lang_config_vis.h	2011-07-11 09:02:04.383276342 +0400
@@ -1,9 +1,9 @@
 /* -*- c -*- */
-/* $Id: lang_config_vis.h 5675 2010-01-19 09:52:11Z cher $ */
+/* $Id: lang_config_vis.h 6325 2011-05-09 06:47:13Z cher $ */
 #ifndef __LANG_CONFIG_VIS_H__
 #define __LANG_CONFIG_VIS_H__
 
-/* Copyright (C) 2008 Alexander Chernov <cher@ejudge.ru> */
+/* Copyright (C) 2008-2011 Alexander Chernov <cher@ejudge.ru> */
 
 /*
  * This program is free software; you can redistribute it and/or modify
@@ -47,7 +47,8 @@ lang_configure_screen(
         const unsigned char *working_dir,
         unsigned char **keys,
         unsigned char **values,
-        const unsigned char *header);
+        const unsigned char *header,
+        int batch_mode);
 void
 lang_configure_batch(
         const unsigned char *script_dir,
diff -ruNp ejudge-2.3.18/Makefile.unix.in ejudge/Makefile.unix.in
--- ejudge-2.3.18/Makefile.unix.in	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/Makefile.unix.in	2011-07-11 09:02:04.194279817 +0400
@@ -1,5 +1,5 @@
 # -*- Makefile -*-
-# $Id: Makefile.unix.in 6185 2011-03-27 15:16:29Z cher $
+# $Id: Makefile.unix.in 6328 2011-05-10 04:25:31Z cher $
 # @configure_input@
 
 # Copyright (C) 2000-2011 Alexander Chernov <cher@ejudge.ru> */
@@ -174,11 +174,12 @@ NSM_OBJECTS = $(NSM_CFILES:.c=.o) libcom
 NSC_CFILES=new-server-control.c version.c
 NSC_OBJECTS=$(NSC_CFILES:.c=.o) libcommon.a libnew_server_clnt.a libplatform.a libcommon.a
 
+INSTALLSCRIPT = ejudge-install.sh
 BINTARGETS = ejudge-jobs-cmd ejudge-edit-users ejudge-setup ejudge-configure-compilers ejudge-control ejudge-execute ejudge-contests-cmd
 SERVERBINTARGETS = ej-compile ej-compile-control ej-run ej-nwrun ej-ncheck ej-batch ej-serve ej-users ej-users-control ej-jobs ej-jobs-control ej-super-server ej-super-server-control ej-contests ej-contests-control uudecode ej-convert-clars ej-convert-runs ej-fix-db
 CGITARGETS = users${CGI_PROG_SUFFIX} serve-control${CGI_PROG_SUFFIX} new-client${CGI_PROG_SUFFIX}
 TARGETS = ${SERVERBINTARGETS} ${BINTARGETS} ${CGITARGETS}
-STYLEFILES = style/logo.gif style/priv.css style/unpriv.css style/priv.js style/unpriv.js style/filter_expr.html
+STYLEFILES = style/logo.gif style/priv.css style/unpriv.css style/priv.js style/unpriv.js style/filter_expr.html style/sprintf.js
 
 all: local_all subdirs_all mo
 local_all: $(TARGETS) ejudge-config
@@ -204,6 +205,8 @@ install_ej_users: ej-users
 	install -m 0755 ej-users "${DESTDIR}${serverbindir}"
 install_ej_contests: ej-contests
 	install -m 0755 ej-contests "${DESTDIR}${serverbindir}"
+install_ej_super_server: ej-super-server
+	install -m 0755 ej-super-server "${DESTDIR}${serverbindir}"
 
 local_install: ${TARGETS} ejudge-config po mo
 	install -d "${DESTDIR}${bindir}"
@@ -230,6 +233,8 @@ local_install: ${TARGETS} ejudge-config 
 	install -m 0755 style/ejudge-upgrade-web "${DESTDIR}${bindir}"
 	mkdir -p "${DESTDIR}${includedir}/ejudge"
 	for i in problem_plugin_impl.h problem_plugin.h ejudge_plugin.h ej_types.h iterators.h contest_plugin.h; do install -m 644 $$i "${DESTDIR}${includedir}/ejudge"; done
+	if [ ! -f "${INSTALLSCRIPT}" ]; then ./ejudge-setup -b; fi
+	if [ -f "${INSTALLSCRIPT}" ]; then install -m 0755 "${INSTALLSCRIPT}" "${DESTDIR}${bindir}"; fi
 
 install: local_install
 	$(MAKE) -C scripts DESTDIR="${DESTDIR}" install
@@ -280,7 +285,7 @@ ejudge-jobs-cmd: ${JP_OBJECTS}
 	${LD} ${LDFLAGS} $^ libcommon.a -o $@ ${LDLIBS} ${EXPAT_LIB}
 
 ej-super-server: ${SS_OBJECTS}
-	${LD} ${LDFLAGS} $^ libcommon.a -o $@ ${LDLIBS} ${EXPAT_LIB}
+	${LD} ${LDFLAGS} -rdynamic $^ libcommon.a -o $@ ${LDLIBS} ${EXPAT_LIB} -ldl
 
 ej-super-server-control: ${SSC_OBJECTS}
 	${LD} ${LDFLAGS} $^ libcommon.a -o $@ ${LDLIBS} ${EXPAT_LIB}
@@ -336,6 +341,9 @@ make-js-actions : make-js-actions.o
 struct-sizes : struct-sizes.o
 	$(LD) $(LDFLAGS) $^ -o $@ $(LDLIBS) ${EXPAT_LIB}
 
+ejudge-install.sh : ejudge-setup
+	./ejudge-setup -b
+
 local_clean:
 	-rm -f *.o *~ *.a $(TARGETS) revinfo version.c $(ARCH)/*.o ejudge.po mkChangeLog2 userlist_clnt/*.o xml_utils/*.o super_clnt/*.o cdeps deps.make filter_expr.[ch] filter_scan.c users users${CGI_PROG_SUFFIX} ejudge-config serve-control serve-control${CGI_PROG_SUFFIX} prjutils/*.o make-js-actions new_server_clnt/*.o mktable struct-sizes
 	-rm -rf locale
@@ -436,7 +444,7 @@ endif
 locale/ru_RU.${CHARSET}/LC_MESSAGES/ejudge.mo : ejudge.ru_RU.${CHARSET}.po ru_all
 	${MSGFMT} -o $@ -c $<
 
-libcommon.a : $(COMMON_CFILES:.c=.o) filter_scan.o filter_expr.o contests_meta.o super-serve_meta.o prepare_meta.o
+libcommon.a : $(COMMON_CFILES:.c=.o) filter_scan.o filter_expr.o contests_meta.o super-serve_meta.o prepare_meta.o super_html_6_meta.o
 	ar rcv $@ $^
 
 libplatform.a : $(PLATFORM_CFILES:.c=.o)
@@ -463,16 +471,14 @@ filter_expr.c filter_expr.h : filter_exp
 filter_scan.c : filter_scan.lex
 	flex -p -s -L -8 -B -o$@ -Pfilter_expr_ $<
 
-contests_meta.c contests_meta.h : contests.h
-	$(META_CC) contests.h -o contests.out --force-h --meta --meta-struct contest_desc --meta-enum-prefix CNTS --meta-func-prefix contest_desc --meta-timestamp
-
-super-serve_meta.c super-serve_meta.h : super-serve.h
-	$(META_CC) super-serve.h -o super-serve.out --force-h --meta --meta-struct sid_state --meta-enum-prefix SSSS --meta-func-prefix ss_sid_state --meta-timestamp
-
-prepare_meta.c prepare_meta.h : prepare.h
-	$(META_CC) prepare.h -o prepare.out --force-h --meta --meta-struct section_global_data --meta-struct section_problem_data --meta-struct section_language_data --meta-struct section_tester_data --meta-enum-prefix CNTSGLOB --meta-enum-prefix CNTSPROB --meta-enum-prefix CNTSLANG --meta-enum-prefix CNTSTESTER --meta-func-prefix cntsglob --meta-func-prefix cntsprob --meta-func-prefix cntslang --meta-func-prefix cntstester --meta-timestamp
+include meta.make
 
 style/actions.js : make-js-actions
 	./make-js-actions > style/actions.js
 
+contest-1/contest-1.c : contest-1/contest-1.tar.gz
+	contest-1/make-c.sh contest-1.tar.gz contest-1/contest-1.tar.gz contest-1/contest-1.c
+contest-1/contest-1.tar.gz :
+	tar cvz --exclude-vcs -C contest-1 -f contest-1/contest-1.tar.gz problems
+
 include deps.make
diff -ruNp ejudge-2.3.18/meta_generic.c ejudge/meta_generic.c
--- ejudge-2.3.18/meta_generic.c	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/meta_generic.c	2011-07-11 09:02:04.662271212 +0400
@@ -1,5 +1,5 @@
 /* -*- mode: c -*- */
-/* $Id: meta_generic.c 6146 2011-03-26 10:47:14Z cher $ */
+/* $Id: meta_generic.c 6286 2011-04-30 20:34:42Z cher $ */
 
 /* Copyright (C) 2008-2011 Alexander Chernov <cher@ejudge.ru> */
 
@@ -124,3 +124,32 @@ meta_lookup_string(const struct meta_aut
   }
   return -atm->st[cur_st][0];
 }
+
+void
+meta_destroy_fields(const struct meta_methods *mth, void *ptr)
+{
+  int field_id, ft;
+  void *fp;
+
+  for (field_id = 1; field_id < mth->last_tag; ++field_id) {
+    ft = mth->get_type(field_id);
+    fp = mth->get_ptr_nc(ptr, field_id);
+    if (!fp) continue;
+    switch (ft) {
+    case '0':                   /* ej_int_opt_0_t */
+    case '3':                   /* ej_checkbox_t */
+    case '4':                   /* ej_int_opt_1_t */
+      break;
+    case '1':                   /* ej_textbox_t */
+    case '2':                   /* ej_textbox_opt_t */
+      {
+        unsigned char **pp = (unsigned char **) fp;
+        xfree(*pp);
+      }
+      break;
+    default:
+      abort();
+    }
+  }
+  memset(ptr, 0, mth->size);
+}
diff -ruNp ejudge-2.3.18/meta_generic.h ejudge/meta_generic.h
--- ejudge-2.3.18/meta_generic.h	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/meta_generic.h	2011-07-11 09:02:04.665271155 +0400
@@ -1,10 +1,10 @@
 /* -*- c -*- */
-/* $Id: meta_generic.h 5363 2008-11-23 17:08:07Z cher $ */
+/* $Id: meta_generic.h 6286 2011-04-30 20:34:42Z cher $ */
 
 #ifndef __META_GENERIC_H__
 #define __META_GENERIC_H__
 
-/* Copyright (C) 2008 Alexander Chernov <cher@ejudge.ru> */
+/* Copyright (C) 2008-2011 Alexander Chernov <cher@ejudge.ru> */
 
 /*
  * This program is free software; you can redistribute it and/or modify
@@ -50,6 +50,8 @@ meta_lookup_string(const struct meta_aut
 
 struct meta_methods
 {
+  int last_tag;
+  size_t size;
   int (*get_type)(int tag);
   size_t (*get_size)(int tag);
   const char *(*get_name)(int tag);
@@ -58,4 +60,7 @@ struct meta_methods
   int (*lookup_field)(const char *name);
 };
 
+void
+meta_destroy_fields(const struct meta_methods *mth, void *ptr);
+
 #endif /* __META_GENERIC_H__ */
diff -ruNp ejudge-2.3.18/meta.make ejudge/meta.make
--- ejudge-2.3.18/meta.make	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/meta.make	2011-07-11 09:02:04.809268510 +0400
@@ -0,0 +1,27 @@
+# -*- Makefile -*-
+# $Id: meta.make 6335 2011-05-18 05:22:54Z cher $
+
+# Copyright (C) 2011 Alexander Chernov <cher@ejudge.ru> */
+
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser 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
+# Lesser General Public License for more details.
+
+contests_meta.c contests_meta.h : contests.h
+	$(META_CC) contests.h -o contests.out --force-h --meta --meta-struct contest_desc --meta-enum-prefix CNTS --meta-func-prefix contest_desc --meta-timestamp
+
+super-serve_meta.c super-serve_meta.h : super-serve.h
+	$(META_CC) super-serve.h -o super-serve.out --force-h --meta --meta-struct sid_state --meta-enum-prefix SSSS --meta-func-prefix ss_sid_state --meta-timestamp
+
+prepare_meta.c prepare_meta.h : prepare.h
+	$(META_CC) prepare.h -o prepare.out --force-h --meta --meta-struct section_global_data --meta-struct section_problem_data --meta-struct section_language_data --meta-struct section_tester_data --meta-enum-prefix CNTSGLOB --meta-enum-prefix CNTSPROB --meta-enum-prefix CNTSLANG --meta-enum-prefix CNTSTESTER --meta-func-prefix cntsglob --meta-func-prefix cntsprob --meta-func-prefix cntslang --meta-func-prefix cntstester --meta-timestamp
+
+super_html_6_meta.c super_html_6_meta.h : super_html_6.h
+	$(META_CC) super_html_6.h -o super_html_6.out --force-h --meta --meta-struct ss_op_param_USER_CREATE_ONE_ACTION --meta-struct ss_op_param_USER_CREATE_MANY_ACTION --meta-struct ss_op_param_USER_CREATE_FROM_CSV_ACTION --meta-struct ss_op_param_USER_CREATE_REG_ACTION --meta-struct ss_op_param_USER_EDIT_REG_ACTION --meta-timestamp
+
diff -ruNp ejudge-2.3.18/mischtml.h ejudge/mischtml.h
--- ejudge-2.3.18/mischtml.h	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/mischtml.h	2011-07-11 09:02:04.430275477 +0400
@@ -1,9 +1,9 @@
 /* -*- c -*- */
-/* $Id: mischtml.h 5675 2010-01-19 09:52:11Z cher $ */
+/* $Id: mischtml.h 6343 2011-05-22 21:20:22Z cher $ */
 #ifndef __MISCHTML_H__
 #define __MISCHTML_H__
 
-/* Copyright (C) 2005-2009 Alexander Chernov <cher@ejudge.ru> */
+/* Copyright (C) 2005-2011 Alexander Chernov <cher@ejudge.ru> */
 
 /*
  * This program is free software; you can redistribute it and/or modify
@@ -29,6 +29,15 @@ unsigned char *html_hyperref(unsigned ch
                              const unsigned char *extra_args,
                              const char *format, ...)
   __attribute__((format(printf, 6, 7)));
+unsigned char *html_hyperref_attr(
+        unsigned char *buf,
+        size_t size,
+        ej_cookie_t session_id,
+        const unsigned char *self_url,
+        const unsigned char *extra_args,
+        const unsigned char *attrs,
+        const char *format, ...)
+  __attribute__((format(printf, 7, 8)));
 
 void html_start_form(FILE *f, int mode,
                      unsigned char const *self_url,
@@ -50,6 +59,15 @@ unsigned char *html_input_text(unsigned 
                                int text_size, const char *format,
                                ...)
   __attribute__((format(printf, 5, 6)));
+unsigned char *html_input_text_js(
+        unsigned char *buf,
+        size_t size,
+        const unsigned char *var_name,
+        int text_size,
+        const unsigned char *onchange,
+        const char *format,
+        ...)
+  __attribute__((format(printf, 6, 7)));
 unsigned char *html_input_password(unsigned char *buf, size_t size,
                                    const unsigned char *var_name,
                                    int text_size, const char *format,
diff -ruNp ejudge-2.3.18/misctext.c ejudge/misctext.c
--- ejudge-2.3.18/misctext.c	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/misctext.c	2011-07-11 09:02:04.552273234 +0400
@@ -1,5 +1,5 @@
 /* -*- mode: c -*- */
-/* $Id: misctext.c 6237 2011-04-08 18:47:35Z cher $ */
+/* $Id: misctext.c 6347 2011-05-23 19:06:12Z cher $ */
 
 /* Copyright (C) 2000-2011 Alexander Chernov <cher@ejudge.ru> */
 
@@ -27,6 +27,7 @@
 #include <stdlib.h>
 #include <time.h>
 #include <ctype.h>
+#include <errno.h>
 
 #include "win32_compat.h"
 
@@ -1351,6 +1352,7 @@ is_empty_string(const unsigned char *s)
   return !*s;
 }
 
+#define SIZE_T (1024L * 1024L * 1024L * 1024L)
 #define SIZE_G (1024 * 1024 * 1024)
 #define SIZE_M (1024 * 1024)
 #define SIZE_K (1024)
@@ -1383,6 +1385,120 @@ size_t_to_size_str(
   return buf;
 }
 
+/*
+  parse an integral value in range -2147483648...2147483647 checking for overflow
+ */
+int
+size_str_to_num(const unsigned char *str, int *p_num)
+{
+  const unsigned char *s;
+  long value;
+  char *eptr = 0;
+  int suffix = 0;
+
+  if (!str) return -1;
+  s = str;
+  while (*s && isspace(*s)) ++s;
+  if (!*s) return -1;
+
+  errno = 0;
+  value = strtol(str, &eptr, 10);
+  if (errno) return -1;
+#if LONG_MAX != INT_MAX
+  if (value < INT_MIN || value > INT_MAX) return -1;
+#endif
+  s = (const unsigned char *) eptr;
+  if (*s && !isspace(*s)) {
+    suffix = toupper(*s);
+    if (suffix != 'K' && suffix != 'M' && suffix != 'G' && suffix != 'T')
+      return -1;
+    ++s; 
+  }
+  while (*s && isspace(*s)) ++s;
+  if (*s) return -1;
+  if (suffix == 'T') {
+    return -1;
+  } else if (suffix == 'G') {
+    if (value < -2 || value > 1) return -1;
+    value *= SIZE_G;
+  } else if (suffix == 'M') {
+    if (value < -2048 || value > 2047) return -1;
+    value *= SIZE_M;
+  } else if (suffix == 'K') {
+    if (value < -2097152 || value > 2097151) return -1;
+    value *= SIZE_K;
+  }
+
+  if (p_num) *p_num = (int) value;
+
+  return 0;
+}
+
+/*
+  parse an integral value in range LONG_MIN ... LONG_MAX checking for overflow 
+ */
+int
+size_str_to_size_t(const unsigned char *str, size_t *p_size)
+{
+  const unsigned char *s;
+  long value;
+  char *eptr = 0;
+  int suffix = 0;
+
+  if (!str) return -1;
+  s = str;
+  while (*s && isspace(*s)) ++s;
+  if (!*s) return -1;
+
+  errno = 0;
+  value = strtol(str, &eptr, 10);
+  if (errno) return -1;
+  s = (const unsigned char *) eptr;
+  if (*s && !isspace(*s)) {
+    suffix = toupper(*s);
+    if (suffix != 'K' && suffix != 'M' && suffix != 'G' && suffix != 'T')
+      return -1;
+    ++s; 
+  }
+  while (*s && isspace(*s)) ++s;
+  if (*s) return -1;
+
+#if INT_MAX == LONG_MAX
+  // 32-bit architecture
+  if (suffix == 'T') {
+    return -1;
+  } else if (suffix == 'G') {
+    if (value < -2 || value > 1) return -1;
+    value *= SIZE_G;
+  } else if (suffix == 'M') {
+    if (value < -2048 || value > 2047) return -1;
+    value *= SIZE_M;
+  } else if (suffix == 'K') {
+    if (value < -2097152 || value > 2097151) return -1;
+    value *= SIZE_K;
+  }
+#else
+  // 64-bit architecture. Note, this will not work for MSVC...
+  if (suffix == 'T') {
+    if (value < -8388608L || value > 8388607L) return -1;
+    value *= SIZE_T;
+  } else if (suffix == 'G') {
+    if (value < -8589934592L || value > 8589934591L) return -1;
+    value *= SIZE_G;
+  } else if (suffix == 'M') {
+    if (value < -8796093022208L || value > 8796093022207L) return -1;
+    value *= SIZE_M;
+  } else if (suffix == 'K') {
+    if (value < -9007199254740992L || value > 9007199254740991L) return -1;
+    value *= SIZE_K;
+  }
+#endif
+
+  if (p_size) *p_size = (size_t) value;
+
+  return 0;
+}
+
 int
 has_control_characters(const unsigned char *str)
 {
@@ -1930,6 +2046,47 @@ ucs2_to_utf8(
 }
 
 /*
+  returns: 0 - address invalid, 1 - valid
+ */
+int
+is_valid_email_address(const unsigned char *email_address)
+{
+  if (!email_address || !*email_address) return 0;
+  const unsigned char *p;
+  const unsigned char *at_ptr = 0;
+  for (p = email_address; *p; ++p) {
+    if (*p <= ' ' || *p >= 127 || *p == '\\' || *p == '\'' || *p == '\"')
+      return 0;
+    if (*p == '@') {
+      if (at_ptr) return 0;
+      at_ptr = p;
+    }
+  }
+  if (!at_ptr) return 0;
+  if (at_ptr == email_address || !at_ptr[1]) return 0;
+  // check domain part
+  for (p = at_ptr + 1; *p; ++p) {
+    if (!isalnum(*p) && *p != '-' && *p != '_' && *p != '.')
+      return 0;
+    if (*p == '.' && (p[-1] == '@' || !p[1] || p[1] == '.')) return 0;
+  }
+  // check local part
+  for (p = email_address; p < at_ptr; ++p) {
+    if (*p == '.') {
+      if (p == email_address || p[1] == '@' || p[1] == '.') return 0;
+    } else if (isalnum(*p)) {
+      // do nothing
+    } else if (*p == '!' || *p == '#' || *p == '+' || *p == '-' || *p == '/' || *p == '=' || *p == '?'
+               || *p == '^' || *p == '_' || *p == '~') {
+      // All valid chars: ! # $ % & * + - / = ? ^ _ ` { | } ~
+    } else {
+      return 0;
+    }
+  }
+  return 1;
+}
+
+/*
  * Local variables:
  *  compile-command: "make"
  *  c-font-lock-extra-types: ("\\sw+_t" "FILE")
diff -ruNp ejudge-2.3.18/misctext.h ejudge/misctext.h
--- ejudge-2.3.18/misctext.h	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/misctext.h	2011-07-11 09:02:04.569272921 +0400
@@ -1,9 +1,9 @@
 /* -*- c -*- */
-/* $Id: misctext.h 6042 2010-11-18 08:48:22Z cher $ */
+/* $Id: misctext.h 6347 2011-05-23 19:06:12Z cher $ */
 #ifndef __MISCTEXT_H__
 #define __MISCTEXT_H__
 
-/* Copyright (C) 2000-2010 Alexander Chernov <cher@ejudge.ru> */
+/* Copyright (C) 2000-2011 Alexander Chernov <cher@ejudge.ru> */
 
 /*
  * This program is free software; you can redistribute it and/or modify
@@ -251,4 +251,12 @@ html_print_by_line(
         unsigned char const *s,
         size_t size);
 
+int
+size_str_to_num(const unsigned char *str, int *p_num);
+int
+size_str_to_size_t(const unsigned char *str, size_t *p_size);
+
+int
+is_valid_email_address(const unsigned char *email_address);
+
 #endif /* __MISCTEXT_H__ */
diff -ruNp ejudge-2.3.18/new-server.c ejudge/new-server.c
--- ejudge-2.3.18/new-server.c	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/new-server.c	2011-07-11 09:02:04.706270403 +0400
@@ -1,5 +1,5 @@
 /* -*- mode: c -*- */
-/* $Id: new-server.c 6162 2011-03-27 07:07:27Z cher $ */
+/* $Id: new-server.c 6323 2011-05-08 20:39:15Z cher $ */
 
 /* Copyright (C) 2006-2011 Alexander Chernov <cher@ejudge.ru> */
 
@@ -57,7 +57,7 @@ static struct server_framework_params pa
   .force_socket_flag = 0,
   .program_name = 0,
   .socket_path = "/tmp/new-server-socket",
-  .log_path = "/tmp/new-server.log",
+  .log_path = "/tmp/ej-contests.log",
   .select_timeout = 1,
   .user_data = 0,
   .startup_error = startup_error,
@@ -612,7 +612,7 @@ setup_log_file(void)
       && os_IsAbsolutePath(ejudge_config->new_server_log))
     return;
   if (ejudge_config->var_dir && os_IsAbsolutePath(ejudge_config->var_dir)) {
-    if (!(s1 = ejudge_config->new_server_log)) s1 = "new-server.log";
+    if (!(s1 = ejudge_config->new_server_log)) s1 = "ej-contests.log";
     snprintf(buf, sizeof(buf), "%s/%s", ejudge_config->var_dir, s1);
     xfree(ejudge_config->new_server_log);
     ejudge_config->new_server_log = xstrdup(buf);
@@ -620,7 +620,7 @@ setup_log_file(void)
   }
   if (ejudge_config->contests_home_dir
       && os_IsAbsolutePath(ejudge_config->contests_home_dir)){
-    if (!(s1 = ejudge_config->new_server_log)) s1 = "new-server.log";
+    if (!(s1 = ejudge_config->new_server_log)) s1 = "ej-contests.log";
     if (!(s2 = ejudge_config->var_dir)) s2 = "var";
     snprintf(buf, sizeof(buf), "%s/%s/%s", ejudge_config->contests_home_dir,
              s2, s1);
@@ -628,7 +628,7 @@ setup_log_file(void)
     ejudge_config->new_server_log = xstrdup(buf);
     return;
   }
-  ejudge_config->new_server_log = xstrdup("/tmp/new-server.log");
+  ejudge_config->new_server_log = xstrdup("/tmp/ej-contests.log");
 }
 
 int
diff -ruNp ejudge-2.3.18/new-server-cmd.c ejudge/new-server-cmd.c
--- ejudge-2.3.18/new-server-cmd.c	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/new-server-cmd.c	2011-07-11 09:02:04.672271028 +0400
@@ -1,5 +1,5 @@
 /* -*- mode: c -*- */
-/* $Id: new-server-cmd.c 6162 2011-03-27 07:07:27Z cher $ */
+/* $Id: new-server-cmd.c 6381 2011-06-25 18:52:12Z cher $ */
 
 /* Copyright (C) 2006-2011 Alexander Chernov <cher@ejudge.ru> */
 
@@ -740,6 +740,7 @@ static const struct command_handler hand
   { "export-xml-runs", prepare_simple, 0, 0, NEW_SRV_ACTION_EXPORT_XML_RUNS},
   { "dump-all-runs", prepare_simple, 0, 0, NEW_SRV_ACTION_VIEW_RUNS_DUMP },
   { "dump-problems", prepare_simple, 0, 0, NEW_SRV_ACTION_DUMP_PROBLEMS },
+  { "dump-languages", prepare_simple, 0, 0, NEW_SRV_ACTION_DUMP_LANGUAGES },
   { "soft-update-stand", prepare_simple, 0, 0, NEW_SRV_ACTION_SOFT_UPDATE_STANDINGS },
   { "suspend-testing", prepare_simple, 0, 0, NEW_SRV_ACTION_TEST_SUSPEND },
   { "resume-testing", prepare_simple, 0, 0, NEW_SRV_ACTION_TEST_RESUME },
@@ -753,6 +754,10 @@ static const struct command_handler hand
   { "team-dump-clar", prepare_clar_id, 0, 0, NEW_SRV_ACTION_DUMP_CLAR },
   { "get-contest-name", prepare_simple, 0, 0, NEW_SRV_ACTION_GET_CONTEST_NAME },
   { "get-contest-type", prepare_simple, 0, 0, NEW_SRV_ACTION_GET_CONTEST_TYPE },
+  { "get-contest-status", prepare_simple, 0, 0, NEW_SRV_ACTION_GET_CONTEST_STATUS },
+  { "get-contest-sched", prepare_simple, 0, 0, NEW_SRV_ACTION_GET_CONTEST_SCHED },
+  { "get-contest-duration", prepare_simple, 0, 0, NEW_SRV_ACTION_GET_CONTEST_DURATION },
+  { "get-contest-description", prepare_simple, 0, 0, NEW_SRV_ACTION_GET_CONTEST_DESCRIPTION },
   { "submit-run", prepare_submit_run, 0, 0, 0 },
   { "team-submit-run", prepare_submit_run, 0, 0, 0 },
   { "import-xml-runs", prepare_import_xml, 0, 0, NEW_SRV_ACTION_UPLOAD_RUNLOG_XML_2 },
diff -ruNp ejudge-2.3.18/new-server.h ejudge/new-server.h
--- ejudge-2.3.18/new-server.h	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/new-server.h	2011-07-11 09:02:04.710270329 +0400
@@ -1,5 +1,5 @@
 /* -*- c -*- */
-/* $Id: new-server.h 6116 2011-03-17 07:41:59Z cher $ */
+/* $Id: new-server.h 6381 2011-06-25 18:52:12Z cher $ */
 
 #ifndef __NEW_SERVER_H__
 #define __NEW_SERVER_H__
@@ -289,6 +289,7 @@ enum
   NEW_SRV_ACTION_UPLOAD_RUNLOG_XML_2,
   NEW_SRV_ACTION_LOGIN,         /* for new-server-cmd */
   NEW_SRV_ACTION_DUMP_PROBLEMS,
+  NEW_SRV_ACTION_DUMP_LANGUAGES,
   NEW_SRV_ACTION_SOFT_UPDATE_STANDINGS,
   NEW_SRV_ACTION_HAS_TRANSIENT_RUNS,
   NEW_SRV_ACTION_DUMP_RUN_STATUS,
@@ -296,6 +297,10 @@ enum
   NEW_SRV_ACTION_DUMP_CLAR,
   NEW_SRV_ACTION_GET_CONTEST_NAME,
   NEW_SRV_ACTION_GET_CONTEST_TYPE,
+  NEW_SRV_ACTION_GET_CONTEST_STATUS,
+  NEW_SRV_ACTION_GET_CONTEST_SCHED,
+  NEW_SRV_ACTION_GET_CONTEST_DURATION,
+  NEW_SRV_ACTION_GET_CONTEST_DESCRIPTION,
   NEW_SRV_ACTION_DUMP_MASTER_RUNS,
   NEW_SRV_ACTION_DUMP_REPORT,
   NEW_SRV_ACTION_FULL_UPLOAD_RUNLOG_XML,
@@ -609,16 +614,29 @@ ns_write_priv_clar(const serve_state_t c
                    struct contest_extra *extra,
                    int clar_id);
 
-void ns_header(FILE *out, unsigned char const *template,
-               unsigned char const *content_type,
-               unsigned char const *charset,
-               const unsigned char *script_part,
-               const unsigned char *body_attr,
-               int locale_id,
-               char const *format, ...)
-  __attribute__((format(printf, 8, 9)));
-void ns_footer(FILE *out, unsigned char const *templ,
-               const unsigned char *copyright, int locale_id);
+void
+ns_header(
+        FILE *out,
+        unsigned char const *template,
+        unsigned char const *content_type,
+        unsigned char const *charset,
+        const unsigned char *script_part,
+        const unsigned char *body_attr,
+        int locale_id,
+        const struct contest_desc *cnts,
+        char const *format,
+        ...)
+  __attribute__((format(printf, 9, 10)));
+void
+ns_separator(
+        FILE *out,
+        unsigned char const *templ,
+        const struct contest_desc *cnts);
+void ns_footer(
+        FILE *out,
+        unsigned char const *templ,
+        const unsigned char *copyright,
+        int locale_id);
 
 const unsigned char *ns_unparse_role(int role);
 
diff -ruNp ejudge-2.3.18/new_server_html_2.c ejudge/new_server_html_2.c
--- ejudge-2.3.18/new_server_html_2.c	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/new_server_html_2.c	2011-07-11 09:02:04.765269318 +0400
@@ -1,5 +1,5 @@
 /* -*- mode: c -*- */
-/* $Id: new_server_html_2.c 6162 2011-03-27 07:07:27Z cher $ */
+/* $Id: new_server_html_2.c 6393 2011-07-04 20:54:33Z cher $ */
 
 /* Copyright (C) 2006-2010 Alexander Chernov <cher@ejudge.ru> */
 
@@ -1089,7 +1089,7 @@ ns_write_priv_source(const serve_state_t
   if (info.lang_id > 0 && info.lang_id <= state->max_lang)
     lang = state->langs[info.lang_id];
 
-  ns_header(f, extra->header_txt, 0, 0, 0, 0, phr->locale_id,
+  ns_header(f, extra->header_txt, 0, 0, 0, 0, phr->locale_id, cnts,
             "%s [%s, %s]: %s %d", ns_unparse_role(phr->role),
             phr->name_arm, extra->contest_arm,
             _("Viewing run"), run_id);
@@ -1643,17 +1643,6 @@ ns_write_priv_source(const serve_state_t
                      "run_id=%d&no_disp=1", run_id));
     } else {
       fprintf(f, "<p>The submission is binary and thus is not shown.</p>\n");
-      /* try to load text description of the archive */
-      txt_flags = archive_make_read_path(state, txt_path, sizeof(txt_path),
-                                         global->report_archive_dir,
-                                         run_id, 0, 0);
-      if (txt_flags >= 0) {
-        if (generic_read_file(&txt_text, 0, &txt_size, txt_flags, 0,
-                              txt_path, 0) >= 0) {
-          fprintf(f, "<pre>%s</pre>\n", ARMOR(txt_text));
-          xfree(txt_text); txt_text = 0; txt_size = 0;
-        }
-      }
     }
   } else if (lang && lang->binary) {
     fprintf(f, "<p>The submission is binary and thus is not shown.</p>\n");
@@ -1696,6 +1685,18 @@ ns_write_priv_source(const serve_state_t
     */
   }
 
+    /* try to load text description of the archive */
+  txt_flags = archive_make_read_path(state, txt_path, sizeof(txt_path),
+                                     global->report_archive_dir,
+                                     run_id, 0, 0);
+  if (txt_flags >= 0) {
+    if (generic_read_file(&txt_text, 0, &txt_size, txt_flags, 0,
+                          txt_path, 0) >= 0) {
+      fprintf(f, "<h2>%s</h2>\n<pre>%s</pre>\n", "Style checker output", ARMOR(txt_text));
+      xfree(txt_text); txt_text = 0; txt_size = 0;
+    }
+  }
+
   fprintf(f, "<h2>%s</h2>\n", _("Send a message about this run"));
   html_start_form_id(f, 1, phr->self_url, "run_comment", phr->hidden_vars);
   html_hidden(f, "run_id", "%d", run_id);
@@ -1815,7 +1816,7 @@ ns_write_priv_report(const serve_state_t
     content_type = get_content_type(rep_text, &start_ptr);
   }
 
-  ns_header(f, extra->header_txt, 0, 0, 0, 0, phr->locale_id,
+  ns_header(f, extra->header_txt, 0, 0, 0, 0, phr->locale_id, cnts,
             "%s [%s, %s]: %s %d", ns_unparse_role(phr->role),
             phr->name_arm, extra->contest_arm,
             team_report_flag?_("Viewing user report"):_("Viewing report"),
@@ -1852,7 +1853,8 @@ ns_write_priv_report(const serve_state_t
       }
     } else {
       if (team_report_flag) {
-        write_xml_team_testing_report(cs, prob, f, 0, re.is_marked, start_ptr, "b1");
+        write_xml_team_testing_report(cs, prob, f, 0, re.is_marked, start_ptr, "b1", phr->session_id, phr->self_url, "",
+                                      new_actions_vector);
       } else {
         write_xml_testing_report(f, 0, start_ptr, phr->session_id,phr->self_url,
                                  "", new_actions_vector, "b1", 0);
@@ -1917,7 +1919,7 @@ ns_write_audit_log(const serve_state_t c
   }
   audit_html = html_armor_string_dup(audit_text);
 
-  ns_header(f, extra->header_txt, 0, 0, 0, 0, phr->locale_id,
+  ns_header(f, extra->header_txt, 0, 0, 0, 0, phr->locale_id, cnts,
             "%s [%s, %s]: %s %d", ns_unparse_role(phr->role),
             phr->name_arm, extra->contest_arm,
             _("Viewing audit log for"), run_id);
@@ -3461,7 +3463,7 @@ ns_write_priv_standings(
 
   if (state->global->score_system == SCORE_KIROV
       || state->global->score_system == SCORE_OLYMPIAD)
-    do_write_kirov_standings(state, cnts, f, 0, 1, 0, 0, 0, 0, 0 /*accepting_mode*/, 1, 0, 0, u, 0 /* user_mode */);
+    do_write_kirov_standings(state, cnts, f, 0, 1, 0, 0, 0, 0, 0, 0 /*accepting_mode*/, 1, 0, 0, u, 0 /* user_mode */);
   else if (state->global->score_system == SCORE_MOSCOW)
     do_write_moscow_standings(state, cnts, f, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0,
                               u);
diff -ruNp ejudge-2.3.18/new_server_html_3.c ejudge/new_server_html_3.c
--- ejudge-2.3.18/new_server_html_3.c	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/new_server_html_3.c	2011-07-11 09:02:04.047282518 +0400
@@ -1,7 +1,7 @@
 /* -*- mode: c -*- */
-/* $Id: new_server_html_3.c 5675 2010-01-19 09:52:11Z cher $ */
+/* $Id: new_server_html_3.c 6353 2011-05-28 07:35:10Z cher $ */
 
-/* Copyright (C) 2006-2009 Alexander Chernov <cher@ejudge.ru> */
+/* Copyright (C) 2006-2011 Alexander Chernov <cher@ejudge.ru> */
 
 /*
  * This program is free software; you can redistribute it and/or modify
@@ -73,7 +73,7 @@ const unsigned char ns_fancy_header[] =
 "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
 "<html><head>\n<meta http-equiv=\"Content-type\" content=\"text/html; charset=%C\"/>\n"
 "%S"
-"<link rel=\"stylesheet\" href=\"" CONF_STYLE_PREFIX "unpriv.css\" type=\"text/css\"/>\n"
+"<link rel=\"stylesheet\" href=\"%Y\" type=\"text/css\"/>\n"
   //"<link rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/favicon.ico\"/>\n"
 "<title>%H</title></head>\n"
   //"<body onload=\"startClock()\">"
@@ -89,7 +89,7 @@ const unsigned char ns_fancy_empty_statu
 "<td class=\"menu\"><div class=\"contest_actions_item\">&nbsp;</div></td></tr></table></div>\n";
 const unsigned char ns_fancy_separator[] =
 "</div>\n"
-"<div id=\"l11\"><img src=\"" CONF_STYLE_PREFIX "logo.gif\" alt=\"ejudge logo\"/></div>\n"
+"<div id=\"l11\"><img src=\"%O\" alt=\"logo\"/></div>\n"
 "<div id=\"l13\">\n";
 const unsigned char ns_fancy_footer[] =
 "<div id=\"footer\">%R</div>\n"
@@ -118,15 +118,18 @@ const unsigned char * const ns_ssl_flag_
 };
 
 static void
-process_template(FILE *out,
-                 unsigned char const *templ,
-                 unsigned char const *content_type,
-                 unsigned char const *charset,
-                 unsigned char const *title,
-                 unsigned char const *copyright,
-                 const unsigned char *script_part,
-                 const unsigned char *body_attr,
-                 int locale_id)
+process_template(
+        FILE *out,
+        unsigned char const *templ,
+        unsigned char const *content_type,
+        unsigned char const *charset,
+        unsigned char const *title,
+        unsigned char const *copyright,
+        const unsigned char *script_part,
+        const unsigned char *body_attr,
+        int locale_id,
+        const unsigned char *logo_url,
+        const unsigned char *css_url)
 {
   unsigned char const *s = templ;
 
@@ -157,6 +160,12 @@ process_template(FILE *out,
     case 'B':
       fputs(body_attr, out);
       break;
+    case 'O':
+      fputs(logo_url, out);
+      break;
+    case 'Y':
+      fputs(css_url, out);
+      break;
     default:
       putc('%', out);
       continue;
@@ -166,16 +175,22 @@ process_template(FILE *out,
 }
 
 void
-ns_header(FILE *out, unsigned char const *templ,
-          unsigned char const *content_type,
-          unsigned char const *charset,
-          const unsigned char *script_part,
-          const unsigned char *body_attr,
-          int locale_id,
-          char const *format, ...)
+ns_header(
+        FILE *out,
+        unsigned char const *templ,
+        unsigned char const *content_type,
+        unsigned char const *charset,
+        const unsigned char *script_part,
+        const unsigned char *body_attr,
+        int locale_id,
+        const struct contest_desc *cnts,
+        char const *format,
+        ...)
 {
   va_list args;
   unsigned char title[1024];
+  const unsigned char *logo_url = 0;
+  const unsigned char *css_url = 0;
 
   title[0] = 0;
   if (format) {
@@ -190,21 +205,63 @@ ns_header(FILE *out, unsigned char const
   if (!script_part) script_part = "";
   if (!body_attr) body_attr = "";
 
+  if (cnts) {
+    logo_url = cnts->logo_url;
+    css_url = cnts->css_url;
+  }
+  if (!logo_url) {
+#if defined CONF_STYLE_PREFIX
+    logo_url = CONF_STYLE_PREFIX "logo.gif";
+#else
+    logo_url = "logo.gif";
+#endif
+  }
+  if (!css_url) {
+#if defined CONF_STYLE_PREFIX
+    css_url = CONF_STYLE_PREFIX "unpriv.css";
+#else
+    css_url = "unpriv.css";
+#endif
+  }
+
   fprintf(out, "Content-Type: %s; charset=%s\n"
           "Cache-Control: no-cache\n"
           "Pragma: no-cache\n\n", content_type, charset);
 
   process_template(out, templ, content_type, charset, title, 0,
-                   script_part, body_attr, locale_id);
+                   script_part, body_attr, locale_id, logo_url, css_url);
+}
+
+void
+ns_separator(
+        FILE *out,
+        unsigned char const *templ,
+        const struct contest_desc *cnts)
+{
+  const unsigned char *logo_url = 0;
+
+  if (cnts) logo_url = cnts->logo_url;
+  if (!logo_url) {
+#if defined CONF_STYLE_PREFIX
+    logo_url = CONF_STYLE_PREFIX "logo.gif";
+#else
+    logo_url = "logo.gif";
+#endif
+  }
+
+  process_template(out, templ, NULL, NULL, NULL, NULL, NULL, NULL, 0, logo_url, NULL);
 }
 
 void
-ns_footer(FILE *out, unsigned char const *templ,
-          const unsigned char *copyright, int locale_id)
+ns_footer(
+        FILE *out,
+        unsigned char const *templ,
+        const unsigned char *copyright,
+        int locale_id)
 {
   if (!copyright) copyright = get_copyright(locale_id);
   if (!templ) templ = ns_default_footer;
-  process_template(out, templ, 0, 0, 0, copyright, 0, 0, 0);
+  process_template(out, templ, 0, 0, 0, copyright, 0, 0, 0, NULL, NULL);
 }
 
 void
@@ -259,10 +316,10 @@ ns_html_err_no_perm(FILE *fout,
     }    
   }
   l10n_setlocale(phr->locale_id);
-  ns_header(fout, header, 0, 0, 0, 0, phr->locale_id, _("Permission denied"));
+  ns_header(fout, header, 0, 0, 0, 0, phr->locale_id, cnts, _("Permission denied"));
   if (separator && *separator) {
     fprintf(fout, "%s", ns_fancy_empty_status);
-    fprintf(fout, "%s", separator);
+    ns_separator(fout, separator, cnts);
   }
   fprintf(fout, "<p>%s</p>\n",
           _("Permission denied. The possible reasons are as follows."));
@@ -354,10 +411,10 @@ ns_html_err_simple_registered(
     }    
   }
   l10n_setlocale(phr->locale_id);
-  ns_header(fout, header, 0, 0, 0, 0, phr->locale_id, _("Cannot participate"));
+  ns_header(fout, header, 0, 0, 0, 0, phr->locale_id, cnts, _("Cannot participate"));
   if (separator && *separator) {
     fprintf(fout, "%s", ns_fancy_empty_status);
-    fprintf(fout, "%s", separator);
+    ns_separator(fout, separator, cnts);
   }
   fprintf(fout, "<p>%s</p>\n",
           _("You cannot participate in this contest. Your account was created using the simple registration procedure, i.e. your e-mail address was not verified. This contest requires e-mail verification, so your account cannot be accepted."));
@@ -441,10 +498,10 @@ ns_html_err_inv_param(FILE *fout,
     }    
   }
   l10n_setlocale(phr->locale_id);
-  ns_header(fout, header, 0, 0, 0, 0, phr->locale_id, _("Invalid parameter"));
+  ns_header(fout, header, 0, 0, 0, 0, phr->locale_id, cnts, _("Invalid parameter"));
   if (separator && *separator) {
     fprintf(fout, "%s", ns_fancy_empty_status);
-    fprintf(fout, "%s", separator);
+    ns_separator(fout, separator, cnts);
   }
   fprintf(fout, "<p>%s</p>\n",
           _("A request parameter is invalid. Please, contact the site administrator."));
@@ -502,10 +559,10 @@ ns_html_err_service_not_available(FILE *
     }    
   }
   l10n_setlocale(phr->locale_id);
-  ns_header(fout, header, 0, 0, 0, 0, phr->locale_id, _("Service not available"));
+  ns_header(fout, header, 0, 0, 0, 0, phr->locale_id, cnts, _("Service not available"));
   if (separator && *separator) {
     fprintf(fout, "%s", ns_fancy_empty_status);
-    fprintf(fout, "%s", separator);
+    ns_separator(fout, separator, cnts);
   }
   fprintf(fout, "<p>%s</p>\n",
           _("Service that you requested is not available."));
@@ -563,10 +620,10 @@ ns_html_err_cnts_unavailable(FILE *fout,
     }    
   }
   l10n_setlocale(phr->locale_id);
-  ns_header(fout, header, 0, 0, 0, 0, phr->locale_id, _("Contest not available"));
+  ns_header(fout, header, 0, 0, 0, 0, phr->locale_id, cnts, _("Contest not available"));
   if (separator && *separator) {
     fprintf(fout, "%s", ns_fancy_empty_status);
-    fprintf(fout, "%s", separator);
+    ns_separator(fout, separator, cnts);
   }
   fprintf(fout, "<p>%s</p>\n",
           _("The contest is temporarily not available. Please, retry the request a bit later."));
@@ -629,11 +686,11 @@ ns_html_err_ul_server_down(FILE *fout,
     }    
   }
   l10n_setlocale(phr->locale_id);
-  ns_header(fout, header, 0, 0, 0, 0, phr->locale_id,
+  ns_header(fout, header, 0, 0, 0, 0, phr->locale_id, cnts,
             _("User database server is down"));
   if (separator && *separator) {
     fprintf(fout, "%s", ns_fancy_empty_status);
-    fprintf(fout, "%s", separator);
+    ns_separator(fout, separator, cnts);
   }
   fprintf(fout, "<p>%s</p>\n",
           _("The user database server is currently not available. Please, retry the request later."));
@@ -696,10 +753,10 @@ ns_html_err_internal_error(FILE *fout,
     }    
   }
   l10n_setlocale(phr->locale_id);
-  ns_header(fout, header, 0, 0, 0, 0, phr->locale_id, _("Internal error"));
+  ns_header(fout, header, 0, 0, 0, 0, phr->locale_id, cnts, _("Internal error"));
   if (separator && *separator) {
     fprintf(fout, "%s", ns_fancy_empty_status);
-    fprintf(fout, "%s", separator);
+    ns_separator(fout, separator, cnts);
   }
   fprintf(fout, "<p>%s</p>\n",
           _("Your request has caused an internal server error. Please, report it as a bug."));
@@ -762,10 +819,10 @@ ns_html_err_inv_session(FILE *fout,
     }    
   }
   l10n_setlocale(phr->locale_id);
-  ns_header(fout, header, 0, 0, 0, 0, phr->locale_id, _("Invalid session"));
+  ns_header(fout, header, 0, 0, 0, 0, phr->locale_id, cnts, _("Invalid session"));
   if (separator && *separator) {
     fprintf(fout, "%s", ns_fancy_empty_status);
-    fprintf(fout, "%s", separator);
+    ns_separator(fout, separator, cnts);
   }
   fprintf(fout, "<p>%s</p>\n",
           _("Invalid session identifier. The possible reasons are as follows."));
@@ -839,10 +896,10 @@ ns_html_err_registration_incomplete(
     else footer = ns_fancy_footer;
   }
   l10n_setlocale(phr->locale_id);
-  ns_header(fout, header, 0, 0, 0, 0, phr->locale_id, _("Registration incomplete"));
+  ns_header(fout, header, 0, 0, 0, 0, phr->locale_id, cnts, _("Registration incomplete"));
   if (separator && *separator) {
     fprintf(fout, "%s", ns_fancy_empty_status);
-    fprintf(fout, "%s", separator);
+    ns_separator(fout, separator, cnts);
   }
 
   if (cnts && cnts->allow_reg_data_edit
@@ -899,10 +956,10 @@ ns_html_err_disqualified(
     else footer = ns_fancy_footer;
   }
   l10n_setlocale(phr->locale_id);
-  ns_header(fout, header, 0, 0, 0, 0, phr->locale_id, _("You are disqualified"));
+  ns_header(fout, header, 0, 0, 0, 0, phr->locale_id, cnts, _("You are disqualified"));
   if (separator && *separator) {
     fprintf(fout, "%s", ns_fancy_empty_status);
-    fprintf(fout, "%s", separator);
+    ns_separator(fout, separator, cnts);
   }
   fprintf(fout, "<p>%s</p>\n",
           _("You are disqualified by the contest administration."));
diff -ruNp ejudge-2.3.18/new_server_html_4.c ejudge/new_server_html_4.c
--- ejudge-2.3.18/new_server_html_4.c	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/new_server_html_4.c	2011-07-11 09:02:04.156280514 +0400
@@ -1,5 +1,5 @@
 /* -*- mode: c -*- */
-/* $Id: new_server_html_4.c 6146 2011-03-26 10:47:14Z cher $ */
+/* $Id: new_server_html_4.c 6381 2011-06-25 18:52:12Z cher $ */
 
 /* Copyright (C) 2006-2011 Alexander Chernov <cher@ejudge.ru> */
 
@@ -56,6 +56,10 @@ parse_int(const char *str, int *p_val)
   int v;
   char *eptr = 0;
 
+  if (!str) return -1;
+  while (isspace(*str)) ++str;
+  if (!*str) return -1;
+
   errno = 0;
   v = strtol(str, &eptr, 10);
   if (errno || *eptr) return -1;
@@ -246,6 +250,24 @@ cmd_dump_problems(
 }
 
 static int
+cmd_dump_languages(
+        FILE *fout,
+        struct http_request_info *phr,
+        const struct contest_desc *cnts,
+        struct contest_extra *extra)
+{
+  const serve_state_t cs = extra->serve_state;
+  int i;
+  const struct section_language_data *lang;
+
+  for (i = 0; i <= cs->max_lang; i++) {
+    if (!(lang = cs->langs[i])) continue;
+    fprintf(fout, "%d;%s;%s;%s;%s\n", lang->id, lang->short_name, lang->long_name, lang->src_sfx, lang->exe_sfx);
+  }
+  return 0;
+}
+
+static int
 do_schedule(
         struct http_request_info *phr,
         serve_state_t cs,
@@ -393,15 +415,8 @@ cmd_operation(
 }
 
 static int
-cmd_operation_2(
-        FILE *fout,
-        struct http_request_info *phr,
-        const struct contest_desc *cnts,
-        struct contest_extra *extra)
+get_contest_type(FILE *fout, const struct section_global_data *global)
 {
-  int retval = 0;
-  const serve_state_t cs = extra->serve_state;
-  const struct section_global_data *global = cs->global;
   const unsigned char *s = 0;
   static const unsigned char * const contest_types[SCORE_TOTAL] =
   {
@@ -418,18 +433,110 @@ cmd_operation_2(
     [SCORE_MOSCOW] = 0,
   };
 
+  if (global->score_system < 0 || global->score_system >= SCORE_TOTAL) return -1;
+  if (global->is_virtual) s = virtual_contest_types[global->score_system];
+  else s = contest_types[global->score_system];
+  if (!s) return -1;
+  fprintf(fout, "%s", s);
+  return 0;
+}
+
+static int
+get_contest_status(
+        FILE *fout,
+        const serve_state_t cs,
+        time_t start_time,
+        time_t stop_time,
+        time_t duration)
+{
+  if (start_time > 0) {
+    if (stop_time > 0) {
+      if (duration == 0 || start_time + duration > cs->current_time) {
+        fprintf(fout, "paused");
+      } else {
+        fprintf(fout, "over");
+      }
+    } else {
+      fprintf(fout, "running");
+    }
+  } else {
+    fprintf(fout, "not started");
+  }
+  return 0;
+}
+
+static int
+get_positive_time(FILE *fout, time_t t)
+{
+  if (t > 0) {
+    fprintf(fout, "%s", xml_unparse_date(t));
+  } else {
+    fprintf(fout, "%d", 0);
+  }
+  return 0;
+}
+
+static int
+get_contest_duration(FILE *fout, int duration)
+{
+  fprintf(fout, "%d", duration);
+  return 0;
+}
+
+static int
+cmd_operation_2(
+        FILE *fout,
+        struct http_request_info *phr,
+        const struct contest_desc *cnts,
+        struct contest_extra *extra)
+{
+  int retval = 0;
+  const serve_state_t cs = extra->serve_state;
+  const struct section_global_data *global = cs->global;
+  time_t start_time = 0, duration = 0, stop_time = 0, sched = 0;
+
+  run_get_times(cs->runlog_state, &start_time, &sched, &duration, &stop_time, 0);	
   switch (phr->action) {
   case NEW_SRV_ACTION_GET_CONTEST_NAME:
     fprintf(fout, "%s", cnts->name);
     break;
   case NEW_SRV_ACTION_GET_CONTEST_TYPE:
-    if (global->score_system < 0
-        || global->score_system >= SCORE_TOTAL)
-      FAIL(NEW_SRV_ERR_INV_CONTEST_ID);
-    if (global->is_virtual) s = virtual_contest_types[global->score_system];
-    else s = contest_types[global->score_system];
-    if (!s) FAIL(NEW_SRV_ERR_INV_CONTEST_ID);
-    fprintf(fout, "%s", s);
+    if (get_contest_type(fout, global) < 0) FAIL(NEW_SRV_ERR_INV_CONTEST_ID);
+    break;
+  case NEW_SRV_ACTION_GET_CONTEST_STATUS:
+    get_contest_status(fout, cs, start_time, stop_time, duration);
+    break;
+  case NEW_SRV_ACTION_GET_CONTEST_SCHED:
+    get_positive_time(fout, sched);
+    break;
+  case NEW_SRV_ACTION_GET_CONTEST_DURATION:
+    get_contest_duration(fout, duration);
+    break;
+  case NEW_SRV_ACTION_GET_CONTEST_DESCRIPTION:
+    /*
+    {
+    'name' : '<name>',
+    'type' : '<type>',
+    'status' : '<status>',
+    'start' : '<start time>',
+    'duration' : '<duration in seconds>',
+    'stop' : '<stop time>',
+    'sched' : '<schedule time>'
+    }
+    */
+    fprintf(fout, "{ 'name' : '%s', 'type' : '", cnts->name);
+    if (get_contest_type(fout, global) < 0) FAIL(NEW_SRV_ERR_INV_CONTEST_ID);
+    fprintf(fout, "', 'status' : '");
+    get_contest_status(fout, cs, start_time, stop_time, duration);
+    fprintf(fout, "', 'start' : '");
+    get_positive_time(fout, start_time);
+    fprintf(fout, "', 'duration' : '");
+    get_contest_duration(fout, duration);
+    fprintf(fout, "', 'stop' : '");
+    get_positive_time(fout, stop_time);
+    fprintf(fout, "', 'sched' : '");
+    get_positive_time(fout, sched);
+    fprintf(fout, "' }");
     break;
   default:
     abort();
@@ -685,12 +792,17 @@ cmd_submit_run(
 
   if (ns_cgi_param(phr, "prob", &s) <= 0)
     FAIL(NEW_SRV_ERR_INV_PROB_ID);
-  for (i = 1; i <= cs->max_prob; i++)
-    if (cs->probs[i] && !strcmp(s, cs->probs[i]->short_name))
-      break;
-  if (i > cs->max_prob)
-    FAIL(NEW_SRV_ERR_INV_PROB_ID);
-  prob = cs->probs[i];
+  if (s && *s == '#') {
+    if (parse_int(s + 1, &i) < 0 || i <= 0 || i > cs->max_prob || !(prob = cs->probs[i]))
+      FAIL(NEW_SRV_ERR_INV_PROB_ID);
+  } else {
+    for (i = 1; i <= cs->max_prob; i++)
+      if (cs->probs[i] && !strcmp(s, cs->probs[i]->short_name))
+        break;
+    if (i > cs->max_prob)
+      FAIL(NEW_SRV_ERR_INV_PROB_ID);
+    prob = cs->probs[i];
+  }
 
   /* check variant */
   switch (phr->role) {
@@ -1690,6 +1802,7 @@ static cmd_handler_t cmd_actions_table[N
   [NEW_SRV_ACTION_VIEW_RUNS_DUMP] = cmd_dump_runs,
   [NEW_SRV_ACTION_LOGOUT] = cmd_logout,
   [NEW_SRV_ACTION_DUMP_PROBLEMS] = cmd_dump_problems,
+  [NEW_SRV_ACTION_DUMP_LANGUAGES] = cmd_dump_languages,
   [NEW_SRV_ACTION_SOFT_UPDATE_STANDINGS] = cmd_operation,
   [NEW_SRV_ACTION_TEST_SUSPEND] = cmd_operation,
   [NEW_SRV_ACTION_TEST_RESUME] = cmd_operation,
@@ -1700,6 +1813,10 @@ static cmd_handler_t cmd_actions_table[N
   [NEW_SRV_ACTION_DUMP_CLAR] = cmd_clar_operation,
   [NEW_SRV_ACTION_GET_CONTEST_NAME] = cmd_operation_2,
   [NEW_SRV_ACTION_GET_CONTEST_TYPE] = cmd_operation_2,
+  [NEW_SRV_ACTION_GET_CONTEST_STATUS] = cmd_operation_2,
+  [NEW_SRV_ACTION_GET_CONTEST_SCHED] = cmd_operation_2,
+  [NEW_SRV_ACTION_GET_CONTEST_DURATION] = cmd_operation_2,
+  [NEW_SRV_ACTION_GET_CONTEST_DESCRIPTION] = cmd_operation_2,
   [NEW_SRV_ACTION_SUBMIT_RUN] = cmd_submit_run,
   [NEW_SRV_ACTION_UPLOAD_RUNLOG_XML_2] = cmd_import_xml_runs,
   [NEW_SRV_ACTION_DUMP_MASTER_RUNS] = cmd_dump_master_runs,
diff -ruNp ejudge-2.3.18/new_server_html_5.c ejudge/new_server_html_5.c
--- ejudge-2.3.18/new_server_html_5.c	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/new_server_html_5.c	2011-07-11 09:02:04.218279374 +0400
@@ -1,5 +1,5 @@
 /* -*- mode: c -*- */
-/* $Id: new_server_html_5.c 6146 2011-03-26 10:47:14Z cher $ */
+/* $Id: new_server_html_5.c 6353 2011-05-28 07:35:10Z cher $ */
 
 /* Copyright (C) 2007-2011 Alexander Chernov <cher@ejudge.ru> */
 
@@ -106,7 +106,7 @@ anon_select_contest_page(FILE *fout, str
   const int *cntslist = 0;
   int cntsnum = 0;
   const unsigned char *cl;
-  const struct contest_desc *cnts;
+  const struct contest_desc *cnts = NULL;
   time_t curtime = time(0);
   int row = 0, i, orig_locale_id, j;
   const unsigned char *s;
@@ -121,7 +121,7 @@ anon_select_contest_page(FILE *fout, str
 
   // even don't know about the contest specific settings
   l10n_setlocale(phr->locale_id);
-  ns_header(fout, ns_fancy_header, 0, 0, 0, 0, phr->locale_id,
+  ns_header(fout, ns_fancy_header, 0, 0, 0, 0, phr->locale_id, NULL,
             _("Contest selection"));
 
   html_start_form(fout, 1, phr->self_url, "");
@@ -140,7 +140,7 @@ anon_select_contest_page(FILE *fout, str
 
   fprintf(fout, "<td class=\"menu\"><div class=\"contest_actions_item\">&nbsp;</div></td></tr></table></div>\n");
 
-  fprintf(fout, "%s", ns_fancy_separator);
+  ns_separator(fout, ns_fancy_separator, cnts);
 
   fprintf(fout, "<h2>%s</h2>\n", _("Select one of available contests"));
 
@@ -231,7 +231,7 @@ login_page(
     s = _("Log in to edit registration data");
     break;
   }
-  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id,
+  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id, cnts,
             "%s [%s]", s, extra->contest_arm);
 
   html_start_form(fout, 1, phr->self_url, "");
@@ -282,7 +282,7 @@ login_page(
     fprintf(fout, "<td class=\"menu\"><div class=\"contest_actions_item\">&nbsp;</div></td>");
   fprintf(fout, "</tr></table></div>\n");
 
-  fprintf(fout, "%s", extra->separator_txt);
+  ns_separator(fout, extra->separator_txt, cnts);
 
   if (phr->action == NEW_SRV_ACTION_REG_ACCOUNT_CREATED_PAGE) {
     fprintf(fout, "<%s>%s</%s>\n", head_style,
@@ -394,7 +394,7 @@ create_autoassigned_account_page(
   if (!email) email = "";
 
   l10n_setlocale(phr->locale_id);
-  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id,
+  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id, cnts,
             "%s [%s]", _("Create user account"),
             extra->contest_arm);
 
@@ -430,7 +430,7 @@ create_autoassigned_account_page(
     fprintf(fout, "<td class=\"menu\"><div class=\"contest_actions_item\">&nbsp;</div></td>");
   fprintf(fout, "</tr></table></div>\n");
 
-  fprintf(fout, "%s", extra->separator_txt);
+  ns_separator(fout, extra->separator_txt, cnts);
 
   if (reg_error || reg_ul_error) {
     if (reg_error < 0) reg_error = -reg_error;
@@ -541,7 +541,7 @@ create_account_page(
   if (!email) email = "";
 
   l10n_setlocale(phr->locale_id);
-  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id,
+  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id, cnts,
             "%s [%s]", _("Create user account"),
             extra->contest_arm);
 
@@ -577,7 +577,7 @@ create_account_page(
     fprintf(fout, "<td class=\"menu\"><div class=\"contest_actions_item\">&nbsp;</div></td>");
   fprintf(fout, "</tr></table></div>\n");
 
-  fprintf(fout, "%s", extra->separator_txt);
+  ns_separator(fout, extra->separator_txt, cnts);
 
   if (reg_error || reg_ul_error) {
     if (reg_error < 0) reg_error = -reg_error;
@@ -684,6 +684,8 @@ create_account(
   if (!r || !email) FAIL2(NEW_SRV_ERR_EMAIL_UNSPECIFIED);
   if (check_str(email, email_accept_chars) < 0)
     FAIL2(NEW_SRV_ERR_EMAIL_INV_CHARS);
+  if (!is_valid_email_address(email))
+    FAIL2(NEW_SRV_ERR_EMAIL_INV_CHARS);
 
   // if neither login nor email are specified, just change the locale
   if (!*login && !*email) {
@@ -1671,7 +1673,7 @@ main_page(
   n = phr->name;
   if (!n || !*n) n = phr->login;
 
-  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id,
+  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id, cnts,
             "%s [%s, %s]", title, ARMOR(n), extra->contest_arm);
 
   shown_items = 0;
@@ -1719,7 +1721,7 @@ main_page(
     fprintf(fout, "<td class=\"menu\"><div class=\"contest_actions_item\">&nbsp;</div></td>");
   fprintf(fout, "</tr></table></div>\n");
   if (extra->separator_txt && *extra->separator_txt) {
-    fprintf(fout, "%s", extra->separator_txt);
+    ns_separator(fout, extra->separator_txt, cnts);
   }
 
   // status row
@@ -2296,7 +2298,7 @@ edit_page(
   n = phr->name;
   if (!n || !*n) n = phr->login;
 
-  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id,
+  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id, cnts,
             "%s [%s, %s]", s, ARMOR(n), extra->contest_arm);
 
   fprintf(fout, "<div class=\"user_actions\"><table class=\"menu\"><tr>");
@@ -2309,7 +2311,7 @@ edit_page(
   fprintf(fout, "<td class=\"menu\"><div class=\"contest_actions_item\">&nbsp;</div></td>");
   fprintf(fout, "</tr></table></div>\n");
   if (extra->separator_txt && *extra->separator_txt) {
-    fprintf(fout, "%s", extra->separator_txt);
+    ns_separator(fout, extra->separator_txt, cnts);
   }
 
   // status row
@@ -2395,7 +2397,7 @@ action_error_page(
   s = _("Operation errors");
   n = phr->name;
   if (!n || !*n) n = phr->login;
-  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id,
+  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id, cnts,
             "%s [%s, %s]", s, ARMOR(n), extra->contest_arm);
 
   fprintf(fout, "<div class=\"user_actions\"><table class=\"menu\"><tr>");
@@ -2408,7 +2410,7 @@ action_error_page(
   fprintf(fout, "<td class=\"menu\"><div class=\"contest_actions_item\">&nbsp;</div></td>");
   fprintf(fout, "</tr></table></div>\n");
   if (extra->separator_txt && *extra->separator_txt) {
-    fprintf(fout, "%s", extra->separator_txt);
+    ns_separator(fout, extra->separator_txt, cnts);
   }
 
   fprintf(fout, "<br><font color=\"red\"><pre>%s</pre></font><br>\n",
diff -ruNp ejudge-2.3.18/new_server_html.c ejudge/new_server_html.c
--- ejudge-2.3.18/new_server_html.c	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/new_server_html.c	2011-07-11 09:02:04.084281838 +0400
@@ -1,5 +1,5 @@
 /* -*- mode: c -*- */
-/* $Id: new_server_html.c 6200 2011-03-29 19:19:28Z cher $ */
+/* $Id: new_server_html.c 6381 2011-06-25 18:52:12Z cher $ */
 
 /* Copyright (C) 2006-2011 Alexander Chernov <cher@ejudge.ru> */
 
@@ -1056,7 +1056,7 @@ privileged_page_login_page(FILE *fout, s
   struct html_armor_buffer ab = HTML_ARMOR_INITIALIZER;
 
   l10n_setlocale(phr->locale_id);
-  ns_header(fout, ns_fancy_priv_header, 0, 0, 0, 0, phr->locale_id, "Login page");
+  ns_header(fout, ns_fancy_priv_header, 0, 0, 0, 0, phr->locale_id, NULL, "Login page");
   html_start_form(fout, 1, phr->self_url, "");
   fprintf(fout, "<table>\n");
   fprintf(fout, "<tr><td>%s:</td><td><input type=\"text\" size=\"32\" name=\"login\"", _("Login"));
@@ -1129,10 +1129,11 @@ html_error_status_page(FILE *fout,
   }
 
   l10n_setlocale(phr->locale_id);
-  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id,
+  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id, NULL,
             _("Operation completed with errors"));
   if (extra->separator_txt && *extra->separator_txt) {
-    fprintf(fout, "%s%s", ns_fancy_empty_status, extra->separator_txt);
+    fprintf(fout, "%s", ns_fancy_empty_status);
+    ns_separator(fout, extra->separator_txt, cnts);
   }
   fprintf(fout, "<font color=\"red\"><pre>%s</pre></font>\n", ARMOR(log_txt));
   fprintf(fout, "<hr>%s%s</a>\n",
@@ -4548,7 +4549,7 @@ priv_confirmation_page(FILE *fout,
   }
 
   l10n_setlocale(phr->locale_id);
-  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id,
+  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id, cnts,
             "%s [%s, %d, %s]: %s &quot;%s&quot;",
             ns_unparse_role(phr->role), phr->name_arm, phr->contest_id,
             extra->contest_arm, _("Confirm action"),
@@ -4795,7 +4796,7 @@ priv_user_detail_page(FILE *fout,
     FAIL(NEW_SRV_ERR_PERMISSION_DENIED);
 
   l10n_setlocale(phr->locale_id);
-  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id,
+  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id, cnts,
             "%s [%s, %d, %s]: %s %d", ns_unparse_role(phr->role),
             phr->name_arm, phr->contest_id, extra->contest_arm,
             _("Details for user "), user_id);
@@ -4821,7 +4822,7 @@ priv_new_run_form_page(FILE *fout,
     FAIL(NEW_SRV_ERR_PERMISSION_DENIED);
 
   l10n_setlocale(phr->locale_id);
-  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id,
+  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id, cnts,
             "%s [%s, %d, %s]: %s", ns_unparse_role(phr->role), phr->name_arm,
             phr->contest_id, extra->contest_arm, _("Add new run"));
   ns_new_run_form(fout, log_f, phr, cnts, extra);
@@ -4849,7 +4850,7 @@ priv_examiners_page(
   */
 
   l10n_setlocale(phr->locale_id);
-  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id,
+  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id, cnts,
             "%s [%s, %d, %s]: %s", ns_unparse_role(phr->role), phr->name_arm,
             phr->contest_id, extra->contest_arm, _("Add new run"));
   ns_examiners_page(fout, log_f, phr, cnts, extra);
@@ -5022,7 +5023,7 @@ priv_view_users_page(
   if (opcaps_check(phr->caps, OPCAP_GET_USER) >= 0) details_allowed = 1;
 
   l10n_setlocale(phr->locale_id);
-  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id,
+  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id, cnts,
             "%s [%s, %d, %s]: %s", ns_unparse_role(phr->role), phr->name_arm,
             phr->contest_id, extra->contest_arm, _("Users page"));
 
@@ -5326,7 +5327,7 @@ priv_view_priv_users_page(FILE *fout,
   qsort(users.v, users.u, sizeof(users.v[0]), priv_user_info_sort_func);
 
   l10n_setlocale(phr->locale_id);
-  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id,
+  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id, cnts,
             "%s [%s, %d, %s]: %s", ns_unparse_role(phr->role), phr->name_arm,
             phr->contest_id, extra->contest_arm, _("Privileged users page"));
 
@@ -5575,7 +5576,7 @@ priv_view_clar(FILE *fout,
   }
 
   l10n_setlocale(phr->locale_id);
-  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id,
+  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id, cnts,
             "%s [%s, %d, %s]: %s %d", ns_unparse_role(phr->role),
             phr->name_arm, phr->contest_id, extra->contest_arm,
             _("Viewing clar"), clar_id);
@@ -5608,7 +5609,7 @@ priv_standings(FILE *fout,
   }
 
   l10n_setlocale(phr->locale_id);
-  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id,
+  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id, cnts,
             "%s [%s, %d, %s]: %s", ns_unparse_role(phr->role), phr->name_arm,
             phr->contest_id, extra->contest_arm, _("Current standings"));
   ns_write_priv_standings(cs, phr, cnts, fout, cs->accepting_mode);
@@ -5667,7 +5668,7 @@ priv_upload_runlog_csv_1(
     FAIL(NEW_SRV_ERR_PERMISSION_DENIED);  
 
   l10n_setlocale(phr->locale_id);
-  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id,
+  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id, cnts,
             "%s [%s, %d, %s]: %s", ns_unparse_role(phr->role), phr->name_arm,
             phr->contest_id, extra->contest_arm, "Add new runs in CSV format");
   html_start_form(fout, 2, phr->self_url, phr->hidden_vars);
@@ -5749,7 +5750,7 @@ priv_upload_runlog_csv_2(
   close_memstream(ff); ff = 0;
 
   l10n_setlocale(phr->locale_id);
-  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id,
+  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id, cnts,
             "%s [%s, %d, %s]: %s", ns_unparse_role(phr->role), phr->name_arm,
             phr->contest_id, extra->contest_arm, _("Adding new runs"));
 
@@ -5795,7 +5796,7 @@ priv_upload_runlog_xml_1(
     FAIL(NEW_SRV_ERR_PERMISSION_DENIED);  
 
   l10n_setlocale(phr->locale_id);
-  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id,
+  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id, cnts,
             "%s [%s, %d, %s]: %s", ns_unparse_role(phr->role), phr->name_arm,
             phr->contest_id, extra->contest_arm, "Merge XML runlog");
   html_start_form(fout, 2, phr->self_url, phr->hidden_vars);
@@ -5848,7 +5849,7 @@ priv_upload_runlog_xml_2(
   close_memstream(ff); ff = 0;
 
   l10n_setlocale(phr->locale_id);
-  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id,
+  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id, cnts,
             "%s [%s, %d, %s]: %s", ns_unparse_role(phr->role), phr->name_arm,
             phr->contest_id, extra->contest_arm, _("Merging runs"));
 
@@ -5907,7 +5908,7 @@ priv_download_runs_confirmation(
   }
 
   l10n_setlocale(phr->locale_id);
-  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id,
+  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id, cnts,
             "%s [%s, %d, %s]: %s", ns_unparse_role(phr->role), phr->name_arm,
             phr->contest_id, extra->contest_arm, "Download runs configuration");
 
@@ -6080,7 +6081,7 @@ priv_upsolving_configuration_1(
   }
 
   l10n_setlocale(phr->locale_id);
-  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id,
+  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id, cnts,
             "%s [%s, %d, %s]: %s", ns_unparse_role(phr->role), phr->name_arm,
             phr->contest_id, extra->contest_arm, "Upsolving configuration");
 
@@ -6215,7 +6216,7 @@ priv_assign_cyphers_1(
     FAIL(NEW_SRV_ERR_PERMISSION_DENIED);  
 
   l10n_setlocale(phr->locale_id);
-  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id,
+  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id, cnts,
             "%s [%s, %d, %s]: %s", ns_unparse_role(phr->role), phr->name_arm,
             phr->contest_id, extra->contest_arm, "Assign cyphers");
   html_start_form(fout, 1, phr->self_url, phr->hidden_vars);
@@ -6344,7 +6345,7 @@ priv_assign_cyphers_2(
   if (r < 0) FAIL(NEW_SRV_ERR_INTERNAL);
 
   l10n_setlocale(phr->locale_id);
-  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id,
+  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id, cnts,
             "%s [%s, %d, %s]: %s", ns_unparse_role(phr->role),
             phr->name_arm, phr->contest_id, extra->contest_arm,
             _("Assigned cyphers"));
@@ -6443,7 +6444,7 @@ priv_view_passwords(FILE *fout,
   } else {
     s = _("Registration passwords");
   }
-  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id,
+  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id, cnts,
             "%s [%s, %d, %s]: %s", ns_unparse_role(phr->role),
             phr->name_arm, phr->contest_id, extra->contest_arm, s);
 
@@ -6469,7 +6470,7 @@ priv_view_online_users(
   if (phr->role < USER_ROLE_JUDGE) FAIL(NEW_SRV_ERR_PERMISSION_DENIED);
 
   l10n_setlocale(phr->locale_id);
-  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id,
+  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id, cnts,
             "%s [%s, %d, %s]: %s", ns_unparse_role(phr->role),
             phr->name_arm, phr->contest_id, extra->contest_arm,
             _("Online users"));
@@ -6494,7 +6495,7 @@ priv_view_user_ips(
   if (phr->role < USER_ROLE_JUDGE) FAIL(NEW_SRV_ERR_PERMISSION_DENIED);
 
   l10n_setlocale(phr->locale_id);
-  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id,
+  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id, cnts,
             "%s [%s, %d, %s]: %s", ns_unparse_role(phr->role),
             phr->name_arm, phr->contest_id, extra->contest_arm,
             _("IP addresses for users"));
@@ -6519,7 +6520,7 @@ priv_view_ip_users(
   if (phr->role < USER_ROLE_JUDGE) FAIL(NEW_SRV_ERR_PERMISSION_DENIED);
 
   l10n_setlocale(phr->locale_id);
-  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id,
+  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id, cnts,
             "%s [%s, %d, %s]: %s", ns_unparse_role(phr->role),
             phr->name_arm, phr->contest_id, extra->contest_arm,
             _("Users for IP addresses"));
@@ -6544,7 +6545,7 @@ priv_view_exam_info(
   if (phr->role < USER_ROLE_JUDGE) FAIL(NEW_SRV_ERR_PERMISSION_DENIED);
 
   l10n_setlocale(phr->locale_id);
-  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id,
+  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id, cnts,
             "%s [%s, %d, %s]: %s", ns_unparse_role(phr->role),
             phr->name_arm, phr->contest_id, extra->contest_arm,
             _("Examination information"));
@@ -6569,7 +6570,7 @@ priv_priority_form(
   if (phr->role != USER_ROLE_ADMIN) FAIL(NEW_SRV_ERR_PERMISSION_DENIED);
 
   l10n_setlocale(phr->locale_id);
-  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id,
+  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id, cnts,
             "%s [%s, %d, %s]: %s", ns_unparse_role(phr->role),
             phr->name_arm, phr->contest_id, extra->contest_arm,
             _("Judging priorities"));
@@ -6594,7 +6595,7 @@ priv_view_testing_queue(
   if (phr->role != USER_ROLE_ADMIN) FAIL(NEW_SRV_ERR_PERMISSION_DENIED);
 
   l10n_setlocale(phr->locale_id);
-  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id,
+  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id, cnts,
             "%s [%s, %d, %s]: %s", ns_unparse_role(phr->role),
             phr->name_arm, phr->contest_id, extra->contest_arm,
             _("Testing queue"));
@@ -6722,7 +6723,7 @@ priv_admin_contest_settings(
   if (phr->role != USER_ROLE_ADMIN) FAIL(NEW_SRV_ERR_PERMISSION_DENIED);
 
   l10n_setlocale(phr->locale_id);
-  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id,
+  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id, cnts,
             "%s [%s, %d, %s]: %s", ns_unparse_role(phr->role),
             phr->name_arm, phr->contest_id, extra->contest_arm,
             _("Contest settings"));
@@ -6779,7 +6780,7 @@ priv_print_user_exam_protocol(
   if (locale_id > 0) l10n_setlocale(0);
 
   l10n_setlocale(phr->locale_id);
-  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id,
+  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id, cnts,
             "%s [%s, %d, %s]: %s", ns_unparse_role(phr->role), phr->name_arm,
             phr->contest_id, extra->contest_arm, _("Printing user protocol"));
 
@@ -6881,7 +6882,7 @@ priv_print_users_exam_protocol(
   if (locale_id > 0) l10n_setlocale(0);
 
   l10n_setlocale(phr->locale_id);
-  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id,
+  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id, cnts,
             "%s [%s, %d, %s]: %s", ns_unparse_role(phr->role), phr->name_arm,
             phr->contest_id, extra->contest_arm, _("Printing user protocol"));
 
@@ -6946,7 +6947,7 @@ priv_print_problem_exam_protocol(
   if (locale_id > 0) l10n_setlocale(0);
 
   l10n_setlocale(phr->locale_id);
-  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id,
+  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id, cnts,
             "%s [%s, %d, %s]: %s", ns_unparse_role(phr->role), phr->name_arm,
             phr->contest_id, extra->contest_arm,_("Printing problem protocol"));
 
@@ -7635,7 +7636,7 @@ priv_submit_page(
   } else {
     snprintf(bb, sizeof(bb), "%s", _("Submit a solution"));
   }
-  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id,
+  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id, cnts,
             "%s [%s, %d, %s]: %s", ns_unparse_role(phr->role),
             phr->name_arm, phr->contest_id, extra->contest_arm, bb);
 
@@ -7957,7 +7958,7 @@ priv_main_page(FILE *fout,
       need_examiners = 1;
 
   l10n_setlocale(phr->locale_id);
-  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id,
+  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id, cnts,
             "%s [%s, %d, %s]: %s", ns_unparse_role(phr->role),
             phr->name_arm, phr->contest_id, extra->contest_arm, _("Main page"));
   fprintf(fout, "<ul>\n");
@@ -9102,7 +9103,7 @@ unpriv_page_forgot_password_1(FILE *fout
   unpriv_load_html_style(phr, cnts, &extra, &cur_time);
 
   l10n_setlocale(phr->locale_id);
-  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id,
+  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id, cnts,
             _("Lost password recovery [%s]"), extra->contest_arm);
 
   // change language button
@@ -9130,7 +9131,7 @@ unpriv_page_forgot_password_1(FILE *fout
 
   //fprintf(fout, "<div class=\"l13\">\n");
   if (extra->separator_txt && *extra->separator_txt)
-    fprintf(fout, "%s", extra->separator_txt);
+    ns_separator(fout, extra->separator_txt, cnts);
 
   fprintf(fout, _("<p class=\"fixed_width\">Password recovery requires several steps. Now, please, specify the <b>login</b> and the <b>e-mail</b>, which was specified when the login was created.</p>\n<p class=\"fixed_width\">Note, that automatic password recovery is not possible for invisible, banned, locked, or privileged users!</p>\n"));
 
@@ -9219,11 +9220,11 @@ unpriv_page_forgot_password_2(FILE *fout
     close_memstream(log_f); log_f = 0;
 
     l10n_setlocale(phr->locale_id);
-    ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id,
-                     _("Password recovery error"));
+    ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id, cnts,
+              _("Password recovery error"));
     fprintf(fout, "%s", ns_fancy_empty_status);
     if (extra->separator_txt && *extra->separator_txt)
-      fprintf(fout, "%s", extra->separator_txt);
+      ns_separator(fout, extra->separator_txt, cnts);
     fprintf(fout, "<p>Password recovery is not possible because of the following error.</p>\n");
     //fprintf(fout, "%s", extra->separator_txt);
     fprintf(fout, "<font color=\"red\"><pre>%s</pre></font>\n", ARMOR(log_txt));
@@ -9233,12 +9234,12 @@ unpriv_page_forgot_password_2(FILE *fout
   }
 
   l10n_setlocale(phr->locale_id);
-  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id,
+  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id, cnts,
             _("Password recovery, stage 1 [%s, %s]"),
             ARMOR(login), extra->contest_arm);
   fprintf(fout, "%s", ns_fancy_empty_status);
   if (extra->separator_txt && *extra->separator_txt)
-    fprintf(fout, "%s", extra->separator_txt);
+    ns_separator(fout, extra->separator_txt, cnts);
 
   fprintf(fout, _("<p class=\"fixed_width\">First stage of password recovery is successful. You should receive an e-mail message with further instructions. <b>Note,</b> that you should confirm password recovery in 24 hours, or operation will be cancelled.</p>"));
 
@@ -9317,11 +9318,11 @@ unpriv_page_forgot_password_3(FILE *fout
     close_memstream(log_f); log_f = 0;
 
     l10n_setlocale(phr->locale_id);
-    ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id,
+    ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id, cnts,
               _("Password recovery error"));
     fprintf(fout, "%s", ns_fancy_empty_status);
     if (extra->separator_txt && *extra->separator_txt)
-      fprintf(fout, "%s", extra->separator_txt);
+      ns_separator(fout, extra->separator_txt, cnts);
     fprintf(fout, "<p>Password recovery is not possible because of the following error.</p>\n");
     //fprintf(fout, "%s", extra->separator_txt);
     fprintf(fout, "<font color=\"red\"><pre>%s</pre></font>\n", ARMOR(log_txt));
@@ -9334,12 +9335,12 @@ unpriv_page_forgot_password_3(FILE *fout
   if (!s || !*s) s = login;
 
   l10n_setlocale(phr->locale_id);
-  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id,
+  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id, cnts,
             _("Password recovery completed [%s, %s]"),
             ARMOR(s), extra->contest_arm);
   fprintf(fout, "%s", ns_fancy_empty_status);
   if (extra->separator_txt && *extra->separator_txt)
-    fprintf(fout, "%s", extra->separator_txt);
+    ns_separator(fout, extra->separator_txt, cnts);
 
   fprintf(fout, _("<p>New password is generated.</p>"));
   fprintf(fout, "<table><tr><td class=\"menu\">%s</td><td class=\"menu\"><tt>%s</tt></td></tr>\n",
@@ -9440,7 +9441,7 @@ unprivileged_page_login_page(FILE *fout,
   }
 
   l10n_setlocale(phr->locale_id);
-  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id,
+  ns_header(fout, extra->header_txt, 0, 0, 0, 0, phr->locale_id, cnts,
             _("User login [%s]"), extra->contest_arm);
 
 
@@ -9520,7 +9521,7 @@ unprivileged_page_login_page(FILE *fout,
 
   fprintf(fout, "</tr></table></div>\n");
   if (extra->separator_txt && *extra->separator_txt)
-    fprintf(fout, "%s", extra->separator_txt);
+    ns_separator(fout, extra->separator_txt, cnts);
 
   watched_file_update(&extra->welcome, cnts->welcome_file, cur_time);
   if (extra->welcome.text && extra->welcome.text[0])
@@ -10932,14 +10933,14 @@ unpriv_view_test(FILE *fout,
                  struct contest_extra *extra)
 {
   const serve_state_t cs = extra->serve_state;
-  const struct section_global_data *global = cs->global;
+  const struct section_problem_data *prob = 0;
   int run_id, test_num, n;
   const unsigned char *s = 0;
   struct run_entry re;
   FILE *log_f = 0;
   char *log_txt = 0;
   size_t log_len = 0;
-  int enable_rep_view;
+  int enable_rep_view = -1;
 
   // run_id, test_num
   if (unpriv_parse_run_id(fout, phr, cnts, extra, &run_id, &re) < 0)
@@ -10949,8 +10950,29 @@ unpriv_view_test(FILE *fout,
     ns_html_err_inv_param(fout, phr, 0, "cannot parse test_num");
     goto cleanup;
   }
+  if (re.prob_id <= 0 || re.prob_id > cs->max_prob || !(prob = cs->probs[re.prob_id])) {
+    ns_html_err_inv_param(fout, phr, 0, "invalid problem");
+    goto cleanup;
+  }
 
-  enable_rep_view = (cs->online_view_report > 0 || (!cs->online_view_report && global->team_enable_rep_view > 0));
+  // report view is explicitly disabled by the current contest setting
+  if (cs->online_view_report < 0) enable_rep_view = 0;
+  // report view is explicitly enabled by the current contest setting
+  //if (cs->online_view_report > 0) enable_rep_view = 1;
+  // report view is disabled by the problem configuration
+  if (enable_rep_view < 0 && prob->team_enable_rep_view <= 0) enable_rep_view = 0;
+  // report view is enabled by the problem configuration
+  if (enable_rep_view < 0 && prob->team_show_judge_report > 0) enable_rep_view = 1;
+  if (enable_rep_view < 0) {
+    int visibility = cntsprob_get_test_visibility(prob, test_num, cs->online_final_visibility);
+    if (visibility == TV_FULLIFMARKED) {
+      visibility = TV_HIDDEN;
+      if (re.is_marked) visibility = TV_FULL;
+    }
+    if (visibility == TV_FULL) enable_rep_view = 1;
+  }
+
+  if (enable_rep_view < 0) enable_rep_view = 0;
 
   log_f = open_memstream(&log_txt, &log_len);
 
@@ -10962,10 +10984,6 @@ unpriv_view_test(FILE *fout,
     ns_error(log_f, NEW_SRV_ERR_PERMISSION_DENIED);
     goto done;
   }
-  if (global->team_show_judge_report <= 0) {
-    ns_error(log_f, NEW_SRV_ERR_PERMISSION_DENIED);
-    goto done;
-  }
   if (re.user_id != phr->user_id) {
     ns_error(log_f, NEW_SRV_ERR_PERMISSION_DENIED);
     goto done;
@@ -11139,7 +11157,7 @@ unpriv_view_report(FILE *fout,
   unpriv_load_html_style(phr, cnts, 0, 0);
   l10n_setlocale(phr->locale_id);
   ns_header(fout, extra->header_txt, 0, 0, phr->script_part, phr->body_attr,
-            phr->locale_id,
+            phr->locale_id, cnts,
             "%s [%s]: %s %d",
             phr->name_arm, extra->contest_arm, _("Report for run"),
             run_id);
@@ -11186,7 +11204,7 @@ unpriv_view_report(FILE *fout,
         write_xml_team_testing_report(cs, prob, fout,
                                       prob->type != PROB_TYPE_STANDARD,
                                       re.is_marked,
-                                      rep_start, "b1");
+                                      rep_start, "b1", phr->session_id, phr->self_url, "", new_actions_vector);
       }
     }
     break;
@@ -11295,8 +11313,7 @@ unpriv_view_clar(FILE *fout,
 
   unpriv_load_html_style(phr, cnts, 0, 0);
   l10n_setlocale(phr->locale_id);
-  ns_header(fout, extra->header_txt, 0, 0, phr->script_part, phr->body_attr,
-            phr->locale_id,
+  ns_header(fout, extra->header_txt, 0, 0, phr->script_part, phr->body_attr, phr->locale_id, cnts,
             "%s [%s]: %s %d",
             phr->name_arm, extra->contest_arm, _("Clarification"),
             clar_id);
@@ -11383,8 +11400,7 @@ unpriv_view_standings(FILE *fout,
   unpriv_load_html_style(phr, cnts, 0, 0);
   l10n_setlocale(phr->locale_id);
   if (start_time <= 0) {
-    ns_header(fout, extra->header_txt, 0, 0, phr->script_part, phr->body_attr,
-              phr->locale_id,
+    ns_header(fout, extra->header_txt, 0, 0, phr->script_part, phr->body_attr, phr->locale_id, cnts,
               "%s [%s]: %s",
               phr->name_arm, extra->contest_arm, _("Standings [not started]"));
     unpriv_page_header(fout, phr, cnts, extra, start_time, stop_time);
@@ -11424,8 +11440,7 @@ unpriv_view_standings(FILE *fout,
     }
   }
 
-  ns_header(fout, extra->header_txt, 0, 0, phr->script_part, phr->body_attr,
-            phr->locale_id,
+  ns_header(fout, extra->header_txt, 0, 0, phr->script_part, phr->body_attr, phr->locale_id, cnts,
             "%s [%s]: %s%s",
             phr->name_arm, extra->contest_arm, _("Standings"), comment);
 
@@ -11446,10 +11461,10 @@ unpriv_view_standings(FILE *fout,
     fprintf(fout, _("<p>Information is not available.</p>"));
   } else if (global->score_system == SCORE_OLYMPIAD) {
     //fprintf(fout, _("<p>Information is not available.</p>"));
-    do_write_kirov_standings(cs, cnts, fout, 0, 1, 1, 0, 0, 0, 0, 1, cur_time,
+    do_write_kirov_standings(cs, cnts, fout, 0, 1, 1, phr->user_id, 0, 0, 0, 0, 1, cur_time,
                              0, NULL, 1 /* user_mode */);
   } else if (global->score_system == SCORE_KIROV) {
-    do_write_kirov_standings(cs, cnts, fout, 0, 1, 1, 0, 0, 0, 0, 1, cur_time,
+    do_write_kirov_standings(cs, cnts, fout, 0, 1, 1, phr->user_id, 0, 0, 0, 0, 1, cur_time,
                              0, NULL, 1 /* user_mode */);
   } else if (global->score_system == SCORE_MOSCOW) {
     do_write_moscow_standings(cs, cnts, fout, 0, 1, 1, phr->user_id,
@@ -11855,7 +11870,7 @@ unpriv_page_header(FILE *fout,
 
     case '3':
       if (extra->separator_txt && *extra->separator_txt) {
-        fprintf(fout, "%s", extra->separator_txt);
+        ns_separator(fout, extra->separator_txt, cnts);
       }
       break;
 
@@ -12445,8 +12460,7 @@ unpriv_main_page(FILE *fout,
   header = gettext(hh);
   if (!header) header = _("Main page");
   unpriv_load_html_style(phr, cnts, 0, 0);
-  ns_header(fout, extra->header_txt, 0, 0, phr->script_part, phr->body_attr,
-            phr->locale_id,
+  ns_header(fout, extra->header_txt, 0, 0, phr->script_part, phr->body_attr, phr->locale_id, cnts,
             "%s [%s]: %s",
             phr->name_arm, extra->contest_arm, header);
 
@@ -13739,7 +13753,7 @@ anon_select_contest_page(FILE *fout, str
 
   // even don't know about the contest specific settings
   l10n_setlocale(phr->locale_id);
-  ns_header(fout, ns_fancy_header, 0, 0, 0, 0, phr->locale_id,
+  ns_header(fout, ns_fancy_header, 0, 0, 0, 0, phr->locale_id, NULL,
             _("Contest selection"));
 
   html_start_form(fout, 1, phr->self_url, "");
@@ -13758,7 +13772,7 @@ anon_select_contest_page(FILE *fout, str
 
   fprintf(fout, "<td class=\"menu\"><div class=\"contest_actions_item\">&nbsp;</div></td></tr></table></div>\n");
 
-  fprintf(fout, "%s", ns_fancy_separator);
+  ns_separator(fout, ns_fancy_separator, NULL);
 
   fprintf(fout, "<h2>%s</h2>\n", _("Select one of available contests"));
 
@@ -14203,6 +14217,7 @@ static const unsigned char * const symbo
   [NEW_SRV_ACTION_UPLOAD_RUNLOG_XML_2] = "UPLOAD_RUNLOG_XML_2",
   [NEW_SRV_ACTION_LOGIN] = "LOGIN",
   [NEW_SRV_ACTION_DUMP_PROBLEMS] = "DUMP_PROBLEMS",
+  [NEW_SRV_ACTION_DUMP_LANGUAGES] = "DUMP_LANGUAGES",
   [NEW_SRV_ACTION_SOFT_UPDATE_STANDINGS] = "SOFT_UPDATE_STANDINGS",
   [NEW_SRV_ACTION_HAS_TRANSIENT_RUNS] = "HAS_TRANSIENT_RUNS",
   [NEW_SRV_ACTION_DUMP_RUN_STATUS] = "DUMP_RUN_STATUS",
@@ -14210,6 +14225,10 @@ static const unsigned char * const symbo
   [NEW_SRV_ACTION_DUMP_CLAR] = "DUMP_CLAR",
   [NEW_SRV_ACTION_GET_CONTEST_NAME] = "GET_CONTEST_NAME",
   [NEW_SRV_ACTION_GET_CONTEST_TYPE] = "GET_CONTEST_TYPE",
+  [NEW_SRV_ACTION_GET_CONTEST_STATUS] = "GET_CONTEST_STATUS",
+  [NEW_SRV_ACTION_GET_CONTEST_SCHED] = "GET_CONTEST_SCHED",
+  [NEW_SRV_ACTION_GET_CONTEST_DURATION] = "GET_CONTEST_DURATION",
+  [NEW_SRV_ACTION_GET_CONTEST_DURATION] = "GET_CONTEST_DESCRIPTION",
   [NEW_SRV_ACTION_DUMP_MASTER_RUNS] = "DUMP_MASTER_RUNS",
   [NEW_SRV_ACTION_DUMP_REPORT] = "DUMP_REPORT",
   [NEW_SRV_ACTION_FULL_UPLOAD_RUNLOG_XML] = "FULL_UPLOAD_RUNLOG_XML",
diff -ruNp ejudge-2.3.18/NEWS.RUS ejudge/NEWS.RUS
--- ejudge-2.3.18/NEWS.RUS	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/NEWS.RUS	2011-07-11 09:02:04.263278546 +0400
@@ -1,5 +1,93 @@
 -*- mode: text; mode: auto-fill; coding: utf-8 -*-
-$Id: NEWS.RUS 6245 2011-04-10 05:24:07Z cher $
+$Id: NEWS.RUS 6397 2011-07-06 05:01:19Z cher $
+
+Версия 2.3.19 (r6397) (20110706)
+================================
+В исходные файлы системы добавлены файлы для пробного турнира.
+Файлы находятся в каталоге contest-1. Файлы пробного турнира
+используют новую раскладку по каталогам. В пробном турнире по
+умолчанию доступ к CGI-программам открыт для всех IP-адресов.
+
+При выполнении настроечного скрипта, сгенерированного программой
+ejudge-setup, выводится предупреждение о том, что все существовавшие
+настройки будут уничтожены.
+
+При сборке системы автоматически генерируется настроечный скрипт
+ejudge-install.sh со значениями по умолчанию. Ранее необходимо
+было запускать ejudge-setup. При необходимости изменить
+параметры по умолчанию все равно может использоваться
+ejudge-setup.
+
+В конфигурационный файл турнира contest.xml добавлены два новых
+элемента <logo_url> и <css_url>, которые позволяют
+специфицировать URL для логотипа данного турнира и URL для
+стилевого файла данного турнира.
+
+Везде, где требуется вводить e-mail, он проверяется на
+соответствие правилам формирования e-mail. 
+
+Добавлена поддержка GNU Haskell (ghc). Данный язык пока помечен
+как небезопасный. Спасибо vrandik.
+
+Добавлена поддержка архивов программ и утилит сборки, таких как
+make.
+
+ej-compile
+----------
+Лог-файл для программы переименован в ej-compile.log. Он
+по-прежнему находится в каталоге CONTESTS_HOME_DIR/compile/var.
+
+Вывод программы проверки стиля на стандартный поток вывода сохраняется
+для последующего показа.
+
+ej-contests
+-----------
+Лог-файл для программы переименован в ej-contests.log.
+
+Текст, выведенный на стандартный вывод программой проверки стиля,
+отображается при просмотре исходного кода посылки привилегированным
+пользователем.
+
+ejudge-contests-cmd
+-------------------
+Добавлены команды 'dump-languages', 'get-contest-status',
+'get-contest-sched', 'get-contest-duration',
+'get-contest-description' на основе патча от germanilyin.
+
+ej-jobs
+-------
+Лог-файл для программы переименован в ej-jobs.log.
+
+ej-super-server
+---------------
+Лог-файл для программы переименован в ej-super-server.log.
+
+Добавлена возможность редактирования базы пользователей через
+веб-интерфейс. Консольная программа ejudge-edit-users более
+поддерживаться не будет.
+
+В таблицу турниров добавлены прямые ссылки на редактирование
+пользователей и редактирование настроек турнира.
+
+ej-users
+--------
+Лог-файл для программы переименован в ej-users.log.
+
+style_archive
+-------------
+Добавлена поддержка для архивов для сборки программ с помощью
+утилит сборки таких, как make. Поддерживаются следующие новые
+переменные окружения и параметры командной строки:
+EJ_MAKE_MODE (-m)	включить режим проверки архива для
+			сборки. По умолчанию включен режим
+                        проверки архива тестов.
+EJ_PROGRAM_DIR (-p)	имя каталога с файлами для сборки,
+                        который должен находится в
+                        архиве. Значение по умолчанию -
+                        solution.
+EJ_SCRIPT_NAME (-s)     название скрипта для сборки, который
+			должен находиться в архиве. Значение по
+                        умолчанию - Makefile.
 
 Версия 2.3.18 (r6245) (20110410)
 ================================
diff -ruNp ejudge-2.3.18/parsecfg.c ejudge/parsecfg.c
--- ejudge-2.3.18/parsecfg.c	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/parsecfg.c	2011-07-11 09:02:04.527273694 +0400
@@ -1,5 +1,5 @@
 /* -*- c -*- */
-/* $Id: parsecfg.c 6146 2011-03-26 10:47:14Z cher $ */
+/* $Id: parsecfg.c 6246 2011-04-15 04:37:15Z cher $ */
 
 /* Copyright (C) 2000-2011 Alexander Chernov <cher@ejudge.ru> */
 
@@ -18,6 +18,7 @@
 #include "parsecfg.h"
 #include "charsets.h"
 #include "xml_utils.h"
+#include "misctext.h"
 
 #include "reuse_xalloc.h"
 #include "reuse_logger.h"
@@ -1001,17 +1002,6 @@ read_comment(FILE *f)
 }
 
 static int
-num_suffix(const unsigned char *str)
-{
-  if (!str[0]) return 1;
-  if (str[1]) return 0; 
-  if (str[0] == 'k' || str[0] == 'K') return 1024;
-  if (str[0] == 'm' || str[0] == 'M') return 1024 * 1024;
-  if (str[0] == 'g' || str[0] == 'G') return 1024 * 1024 * 1024;
-  return 0;
-}
-
-static int
 copy_param(void *cfg, const struct config_parse_info *params,
            char *varname, char *varvalue)
 {
@@ -1044,40 +1034,22 @@ copy_param(void *cfg, const struct confi
     ptr = (time_t*) ((char*) cfg + params[i].offset);
     *ptr = v;
   } else if (!strcmp(params[i].type, "z")) {
-    int n, m;
-    size_t v, *ptr;
+    size_t v = 0, *ptr = 0;
 
-#ifdef __WIN32__
-    if (sscanf(varvalue, "%d%n", &v, &n) != 1
-        || !(m = num_suffix(varvalue + n))) {
-      fprintf(stderr, "%d: size parameter expected for '%s'\n",
+    if (size_str_to_size_t(varvalue, &v) < 0) {
+      fprintf(stderr, "%d: invalid value of size parameter for '%s'\n",
               parsecfg_state.lineno - 1, varname);
-      return -1;
+      return -1;              
     }
-#else
-    if (sscanf(varvalue, "%zu%n", &v, &n) != 1
-        || !(m = num_suffix(varvalue + n))) {
-      fprintf(stderr, "%d: size parameter expected for '%s'\n",
-              parsecfg_state.lineno - 1, varname);
-      return -1;
-    }
-#endif /* __WIN32__ */
-    // FIXME: check for overflow 
-    v *= m;
     ptr = (size_t*) ((char*) cfg + params[i].offset);
     *ptr = v;
   } else if (!strcmp(params[i].type, "d")) {
-    int  n, v, m;
-    int *ptr;
-
-    if (sscanf(varvalue, "%d%n", &v, &n) != 1
-        || !(m = num_suffix(varvalue + n))) {
-      fprintf(stderr, "%d: numeric parameter expected for '%s'\n",
+    int v = 0, *ptr = 0;
+    if (size_str_to_num(varvalue, &v) < 0) {
+      fprintf(stderr, "%d: invalid value of numeric parameter for '%s'\n",
               parsecfg_state.lineno - 1, varname);
       return -1;
     }
-    // FIXME: check for overflow 
-    v *= m;
     ptr = (int *) ((char*) cfg + params[i].offset);
     *ptr = v;
   } else if (!strcmp(params[i].type, "s")) {
diff -ruNp ejudge-2.3.18/plugins/mysql-userlist/ChangeLog ejudge/plugins/mysql-userlist/ChangeLog
--- ejudge-2.3.18/plugins/mysql-userlist/ChangeLog	2011-04-10 09:25:23.000000000 +0400
+++ ejudge/plugins/mysql-userlist/ChangeLog	2011-07-11 09:02:03.398294446 +0400
@@ -1,3 +1,58 @@
+2011-06-19 (r6376) Alexander Chernov  <cher@ejudge.ru>
+
+	* uldb_mysql.c:
+	(get_brief_list_iterator_2_func) performance is improved, group_id is supported.
+
+2011-05-14 (r6332) Alexander Chernov  <cher@ejudge.ru>
+
+	* uldb_mysql.c:
+	(set_reg_flags_func) copy operation (4) is supported.
+
+2011-05-08 (r6313) Alexander Chernov  <cher@ejudge.ru>
+
+	* methods.inc.c:
+	(get_prev_user_id_func) new function prototype;
+	(get_next_user_id_func) new function prototype.
+
+	* uldb_mysql.c:
+	(get_prev_user_id_func) new function;
+	(get_next_user_id_func) new function.
+
+2011-05-01 (r6288) Alexander Chernov  <cher@ejudge.ru>
+
+	* methods.inc.c, uldb_mysql.c:
+	(new_user_func) many new parameters are added;
+	(register_contest_func) new parameter 'flags' is added.
+
+2011-04-22 (r6261) Alexander Chernov  <cher@ejudge.ru>
+
+	* methods.inc.c:
+	(get_brief_list_iterator_2_func) 'group_id' parameter is added;
+	(get_group_iterator_2_func) new function prototype;
+	(get_group_count_func) new function prototope.
+
+	* uldb_mysql.c:
+	(get_member_serial_func) a bug is fixed;
+	(get_brief_list_iterator_2_func) 'group_id' parameter is added;
+	(get_group_iterator_2_func) new function;
+	(get_group_count_func) new function.
+
+2011-04-20 (r6253) Alexander Chernov  <cher@ejudge.ru>
+
+	* methods.inc.c:
+	(get_user_count_func) new function prototype.
+
+	* uldb_mysql.c:
+	(get_user_count_func) new function.
+
+2011-04-19 (r6250) Alexander Chernov  <cher@ejudge.ru>
+
+	* methods.inc.c: copyright notice is updated;
+	(get_brief_list_iterator_2_func) function prototype is added.
+
+	* uldb_mysql.c:
+	(get_brief_list_iterator_2_func) new function is added.
+
 2011-03-27 (r6188) Alexander Chernov  <cher@ejudge.ru>
 
 	* main.make, Makefile.in:
diff -ruNp ejudge-2.3.18/plugins/mysql-userlist/create-userdb.sql ejudge/plugins/mysql-userlist/create-userdb.sql
--- ejudge-2.3.18/plugins/mysql-userlist/create-userdb.sql	2011-04-10 09:25:23.000000000 +0400
+++ ejudge/plugins/mysql-userlist/create-userdb.sql	2011-07-11 09:02:03.390294592 +0400
@@ -24,7 +24,7 @@ CREATE TABLE %slogins
 
 CREATE TABLE %scookies
        (cookie VARCHAR(32) NOT NULL PRIMARY KEY,
-       user_id INT NOT NULL,
+       user_id INT UNSIGNED NOT NULL,
        contest_id INT UNSIGNED NOT NULL,
        priv_level TINYINT NOT NULL DEFAULT 0,
        role_id TINYINT NOT NULL DEFAULT 0,
@@ -144,10 +144,10 @@ CREATE TABLE %smembers
 
 CREATE TABLE %sgroups
 (
-    group_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
+    group_id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
     group_name VARCHAR(128) NOT NULL UNIQUE KEY,
     description VARCHAR(512) DEFAULT NULL,
-    created_by INT NOT NULL,
+    created_by INT UNSIGNED NOT NULL,
     create_time DATETIME NOT NULL,
     last_change_time DATETIME DEFAULT NULL,
     FOREIGN KEY (created_by) REFERENCES logins(user_id)
@@ -155,8 +155,8 @@ CREATE TABLE %sgroups
 
 CREATE TABLE %sgroupmembers
 (
-    group_id INT NOT NULL,
-    user_id INT NOT NULL,
+    group_id INT UNSIGNED NOT NULL,
+    user_id INT UNSIGNED NOT NULL,
     rights VARCHAR(512) DEFAULT NULL,
     PRIMARY KEY (group_id, user_id),
     FOREIGN KEY g(group_id) REFERENCES groups(group_id),
diff -ruNp ejudge-2.3.18/plugins/mysql-userlist/methods.inc.c ejudge/plugins/mysql-userlist/methods.inc.c
--- ejudge-2.3.18/plugins/mysql-userlist/methods.inc.c	2011-04-10 09:25:23.000000000 +0400
+++ ejudge/plugins/mysql-userlist/methods.inc.c	2011-07-11 09:02:03.417294096 +0400
@@ -1,7 +1,7 @@
 /* -*- mode: c -*- */
-/* $Id: methods.inc.c 5811 2010-05-28 15:14:24Z cher $ */
+/* $Id: methods.inc.c 6313 2011-05-08 06:57:00Z cher $ */
 
-/* Copyright (C) 2008-2010 Alexander Chernov <cher@ejudge.ru> */
+/* Copyright (C) 2008-2011 Alexander Chernov <cher@ejudge.ru> */
 
 /*
  * This program is free software; you can redistribute it and/or modify
@@ -56,8 +56,17 @@ new_user_func(
         void *data,
         const unsigned char *login,
         const unsigned char *email,
+        int passwd_method,
         const unsigned char *passwd,
-        int simple_reg_flag);
+        int is_privileged,
+        int is_invisible,
+        int is_banned,
+        int is_locked,
+        int show_login,
+        int show_email,
+        int read_only,
+        int never_clean,
+        int simple_registration);
 static int
 remove_user_func(
         void *data,
@@ -189,6 +198,7 @@ register_contest_func(
         int user_id,
         int contest_id,
         int status,
+        int flags,
         time_t cur_time,
         const struct userlist_contest **p_c);
 static int
@@ -435,6 +445,48 @@ static int
 create_group_member_func(void *data, int group_id, int user_id);
 static int
 remove_group_member_func(void *data, int group_id, int user_id);
+static ptr_iterator_t
+get_brief_list_iterator_2_func(
+        void *data,
+        int contest_id,
+        int group_id,
+        const unsigned char *filter,
+        int offset,
+        int count);
+static int
+get_user_count_func(
+        void *data,
+        int contest_id,
+        int group_id,
+        const unsigned char *filter,
+        long long *p_count);
+static ptr_iterator_t
+get_group_iterator_2_func(
+        void *data,
+        const unsigned char *filter,
+        int offset,
+        int count);
+static int
+get_group_count_func(
+        void *data,
+        const unsigned char *filter,
+        long long *p_count);
+static int
+get_prev_user_id_func(
+        void *data,
+        int contest_id,
+        int group_id,
+        int user_id,
+        const unsigned char *filter,
+        int *p_user_id);
+static int
+get_next_user_id_func(
+        void *data,
+        int contest_id,
+        int group_id,
+        int user_id,
+        const unsigned char *filter,
+        int *p_user_id);
 
 /*
  * Local variables:
diff -ruNp ejudge-2.3.18/plugins/mysql-userlist/uldb_mysql.c ejudge/plugins/mysql-userlist/uldb_mysql.c
--- ejudge-2.3.18/plugins/mysql-userlist/uldb_mysql.c	2011-04-10 09:25:23.000000000 +0400
+++ ejudge/plugins/mysql-userlist/uldb_mysql.c	2011-07-11 09:02:03.414294150 +0400
@@ -1,5 +1,5 @@
 /* -*- mode: c -*- */
-/* $Id: uldb_mysql.c 6153 2011-03-27 06:52:33Z cher $ */
+/* $Id: uldb_mysql.c 6376 2011-06-19 19:43:53Z cher $ */
 
 /* Copyright (C) 2006-2011 Alexander Chernov <cher@ejudge.ru> */
 
@@ -228,6 +228,18 @@ struct uldb_plugin_iface plugin_uldb_mys
   create_group_member_func,
   // remove a group member
   remove_group_member_func,
+  // get a user iterator
+  get_brief_list_iterator_2_func,
+  // get the total count of the users
+  get_user_count_func,
+  // get a group iterator
+  get_group_iterator_2_func,
+  // get the total count of the groups
+  get_group_count_func,
+  // get the previous user
+  get_prev_user_id_func,
+  // get the next user
+  get_next_user_id_func,
 };
 
 // the size of the cookies pool, must be power of 2
@@ -1032,8 +1044,17 @@ new_user_func(
         void *data,
         const unsigned char *login,
         const unsigned char *email,
+        int passwd_method,
         const unsigned char *passwd,
-        int simple_reg_flag)
+        int is_privileged,
+        int is_invisible,
+        int is_banned,
+        int is_locked,
+        int show_login,
+        int show_email,
+        int read_only,
+        int never_clean,
+        int simple_registration)
 {
   struct uldb_mysql_state *state = (struct uldb_mysql_state*) data;
   int val, inserted_flag = 0;
@@ -1051,7 +1072,16 @@ new_user_func(
     user.login = (char*) login;
     user.email = (char*) email;
     user.passwd = (char*) passwd;
-    user.simple_registration = !!simple_reg_flag;
+    user.passwd_method = passwd_method;
+    user.is_privileged = !!is_privileged;
+    user.is_invisible = !!is_invisible;
+    user.is_banned = !!is_banned;
+    user.is_locked = !!is_locked;
+    user.show_login = !!show_login;
+    user.show_email = !!show_email;
+    user.read_only = !!read_only;
+    user.never_clean = !!never_clean;
+    user.simple_registration = !!simple_registration;
 
     cmd_f = open_memstream(&cmd_t, &cmd_z);
     fprintf(cmd_f, "INSERT into %slogins VALUES ( ", state->md->table_prefix);
@@ -1070,7 +1100,16 @@ new_user_func(
     user.login = (char*) login;
     user.email = (char*) email;
     user.passwd = (char*) passwd;
-    user.simple_registration = !!simple_reg_flag;
+    user.passwd_method = passwd_method;
+    user.is_privileged = !!is_privileged;
+    user.is_invisible = !!is_invisible;
+    user.is_banned = !!is_banned;
+    user.is_locked = !!is_locked;
+    user.show_login = !!show_login;
+    user.show_email = !!show_email;
+    user.read_only = !!read_only;
+    user.never_clean = !!never_clean;
+    user.simple_registration = !!simple_registration;
 
     cmd_f = open_memstream(&cmd_t, &cmd_z);
     fprintf(cmd_f, "INSERT into %slogins VALUES ( ", state->md->table_prefix);
@@ -2425,6 +2464,7 @@ register_contest_func(
         int user_id,
         int contest_id,
         int status,
+        int flags,
         time_t cur_time,
         const struct userlist_contest **p_c)
 {
@@ -2445,6 +2485,7 @@ register_contest_func(
   memset(&new_uc, 0, sizeof(new_uc));
   new_uc.id = contest_id;
   new_uc.status = status;
+  new_uc.flags = flags;
   new_uc.create_time = cur_time;
   new_uc.last_change_time = cur_time;
   cmd_f = open_memstream(&cmd_t, &cmd_z);
@@ -2899,6 +2940,18 @@ set_reg_flags_func(
       sep = ", ";
     }
     break;
+  case 4:                       /* copy */
+    fprintf(cmd_f, "%sinvisible = %d", sep, !!(value & USERLIST_UC_INVISIBLE));
+    sep = ", ";
+    fprintf(cmd_f, "%sbanned = %d", sep, !!(value & USERLIST_UC_BANNED));
+    sep = ", ";
+    fprintf(cmd_f, "%slocked = %d", sep, !!(value & USERLIST_UC_LOCKED));
+    sep = ", ";
+    fprintf(cmd_f, "%sincomplete = %d", sep, !!(value & USERLIST_UC_INCOMPLETE));
+    sep = ", ";
+    fprintf(cmd_f, "%sdisqualified = %d", sep, !!(value & USERLIST_UC_DISQUALIFIED));
+    sep = ", ";
+    break;
   default:
     abort();
   }
@@ -3891,7 +3944,10 @@ get_member_serial_func(void *data)
   snprintf(cmdbuf, sizeof(cmdbuf), "SELECT MAX(serial) FROM %smembers WHERE 1 ;", state->md->table_prefix);
   cmdlen = strlen(cmdbuf);
   if (state->mi->query_one_row(state->md, cmdbuf, cmdlen, 1) < 0) goto fail;
-  if (state->mi->int_val(state->md, &current_member, 1) < 0) goto fail;
+  if (!state->md->lengths[0])
+    db_error_inv_value_fail(state->md, "value");
+  if (state->mi->parse_int(state->md, state->md->row[0], &current_member) < 0 || current_member <= 0)
+    db_error_inv_value_fail(state->md, "value");
   state->mi->free_res(state->md);
   return current_member;
 
@@ -4810,6 +4866,453 @@ remove_group_member_func(void *data, int
   return 0;
 }
 
+static ptr_iterator_t
+get_brief_list_iterator_2_func(
+        void *data,
+        int contest_id,
+        int group_id,
+        const unsigned char *filter,
+        int offset,
+        int count)
+{
+  struct uldb_mysql_state *state = (struct uldb_mysql_state*) data;
+  struct brief_list_iterator *iter = 0;
+  int i, val, j;
+  FILE *uid_f = NULL;
+  char *uid_t = NULL;
+  size_t uid_z = 0;
+
+  if (offset < 0) offset = 0;
+  if (count < 0) count = 0;
+  if (offset + count < 0) count = 0;
+
+  XCALLOC(iter, 1);
+  iter->b = brief_list_iterator_funcs;
+  iter->state = state;
+  iter->contest_id = contest_id;
+  iter->cur_ind = 0;
+
+  if (contest_id <= 0 && group_id > 0) {
+    if (state->mi->fquery(state->md, LOGIN_WIDTH,
+                  "SELECT l.* FROM %slogins AS l, %sgroupmembers AS m WHERE l.user_id = m.user_id AND m.group_id = %d ORDER BY l.user_id LIMIT %d, %d;",
+                          state->md->table_prefix,
+                          state->md->table_prefix,
+                          group_id, offset, count) < 0)
+      goto fail;
+    iter->total_ids = state->md->row_count;
+    if (!iter->total_ids) {
+      state->mi->free_res(state->md);
+      return (ptr_iterator_t) iter;
+    }
+
+    uid_f = open_memstream(&uid_t, &uid_z);
+    XCALLOC(iter->noreg_rows, iter->total_ids);
+    for (i = 0; i < iter->total_ids; i++) {
+      if (!(state->md->row = mysql_fetch_row(state->md->res)))
+        db_error_no_data_fail(state->md);
+      state->md->lengths = mysql_fetch_lengths(state->md->res);
+      if (!state->md->lengths[0])
+        db_error_inv_value_fail(state->md, "value");
+      if (state->mi->parse_int(state->md, state->md->row[0], &val) < 0 || val <= 0)
+        db_error_inv_value_fail(state->md, "value");
+      iter->noreg_rows[i].user_id = val;
+      copy_saved_row(state, &iter->noreg_rows[i].login_row);
+      if (i > 0) fprintf(uid_f, ", ");
+      fprintf(uid_f, "%d", val);
+    }
+    fclose(uid_f); uid_f = NULL;
+
+    if (!uid_t && !*uid_t) {
+      xfree(uid_t); uid_t = 0;
+      state->mi->free_res(state->md);
+      return (ptr_iterator_t) iter;
+    }
+
+    state->mi->free_res(state->md);
+    if (state->mi->fquery(state->md, USER_INFO_WIDTH,
+                  "SELECT * FROM %susers WHERE contest_id = 0 AND user_id IN (%s) ORDER BY user_id;",
+                          state->md->table_prefix, uid_t) < 0)
+      goto fail;
+    xfree(uid_t); uid_t = 0; uid_z = 0;
+    j = 0;
+    for (i = 0; i < state->md->row_count; i++) {
+      if (!(state->md->row = mysql_fetch_row(state->md->res)))
+        db_error_no_data_fail(state->md);
+      state->md->lengths = mysql_fetch_lengths(state->md->res);
+      if (!state->md->lengths[0])
+        db_error_inv_value_fail(state->md, "value");
+      if (state->mi->parse_int(state->md, state->md->row[0], &val) < 0 || val <= 0)
+        db_error_inv_value_fail(state->md, "value");
+      while (j < iter->total_ids && iter->noreg_rows[j].user_id < val) j++;
+      if (j < iter->total_ids && iter->noreg_rows[j].user_id == val) {
+        copy_saved_row(state, &iter->noreg_rows[j].user_info_row);
+      }
+    }
+
+    state->mi->free_res(state->md);
+    return (ptr_iterator_t) iter;
+  }
+
+  if (!contest_id) {
+    if (state->mi->fquery(state->md, LOGIN_WIDTH,
+                  "SELECT * FROM %slogins WHERE 1 ORDER BY user_id LIMIT %d, %d;",
+                          state->md->table_prefix, offset, count) < 0)
+      goto fail;
+    iter->total_ids = state->md->row_count;
+    if (!iter->total_ids) {
+      state->mi->free_res(state->md);
+      return (ptr_iterator_t) iter;
+    }
+
+    uid_f = open_memstream(&uid_t, &uid_z);
+    XCALLOC(iter->noreg_rows, iter->total_ids);
+    for (i = 0; i < iter->total_ids; i++) {
+      if (!(state->md->row = mysql_fetch_row(state->md->res)))
+        db_error_no_data_fail(state->md);
+      state->md->lengths = mysql_fetch_lengths(state->md->res);
+      if (!state->md->lengths[0])
+        db_error_inv_value_fail(state->md, "value");
+      if (state->mi->parse_int(state->md, state->md->row[0], &val) < 0 || val <= 0)
+        db_error_inv_value_fail(state->md, "value");
+      iter->noreg_rows[i].user_id = val;
+      copy_saved_row(state, &iter->noreg_rows[i].login_row);
+      if (i > 0) fprintf(uid_f, ", ");
+      fprintf(uid_f, "%d", val);
+    }
+    fclose(uid_f); uid_f = NULL;
+
+    if (!uid_t && !*uid_t) {
+      state->mi->free_res(state->md);
+      return (ptr_iterator_t) iter;
+    }
+
+    state->mi->free_res(state->md);
+    if (state->mi->fquery(state->md, USER_INFO_WIDTH,
+                  "SELECT * FROM %susers WHERE contest_id = 0 AND user_id IN (%s) ORDER BY user_id ;",
+                          state->md->table_prefix, uid_t) < 0)
+      goto fail;
+    xfree(uid_t); uid_t = 0; uid_z = 0;
+    j = 0;
+    for (i = 0; i < state->md->row_count; i++) {
+      if (!(state->md->row = mysql_fetch_row(state->md->res)))
+        db_error_no_data_fail(state->md);
+      state->md->lengths = mysql_fetch_lengths(state->md->res);
+      if (!state->md->lengths[0])
+        db_error_inv_value_fail(state->md, "value");
+      if (state->mi->parse_int(state->md, state->md->row[0], &val) < 0 || val <= 0)
+        db_error_inv_value_fail(state->md, "value");
+      while (j < iter->total_ids && iter->noreg_rows[j].user_id < val) j++;
+      if (j < iter->total_ids && iter->noreg_rows[j].user_id == val) {
+        copy_saved_row(state, &iter->noreg_rows[j].user_info_row);
+      }
+    }
+
+    state->mi->free_res(state->md);
+    return (ptr_iterator_t) iter;
+  }
+
+  if (state->mi->fquery(state->md, LOGIN_WIDTH,
+                        "SELECT %slogins.* FROM %slogins, %scntsregs WHERE %slogins.user_id = %scntsregs.user_id AND %scntsregs.contest_id = %d ORDER BY %slogins.user_id LIMIT %d, %d;",
+                        state->md->table_prefix, state->md->table_prefix, state->md->table_prefix,
+                        state->md->table_prefix, state->md->table_prefix, state->md->table_prefix,
+                        contest_id, state->md->table_prefix, offset, count) < 0)
+    goto fail;
+
+  iter->total_ids = state->md->row_count;
+  if (!iter->total_ids) {
+    state->mi->free_res(state->md);
+    return (ptr_iterator_t) iter;
+  }
+
+  uid_f = open_memstream(&uid_t, &uid_z);
+  XCALLOC(iter->full_rows, iter->total_ids);
+  for (i = 0; i < iter->total_ids; i++) {
+    if (!(state->md->row = mysql_fetch_row(state->md->res)))
+      db_error_no_data_fail(state->md);
+    state->md->lengths = mysql_fetch_lengths(state->md->res);
+    if (!state->md->lengths[0])
+      db_error_inv_value_fail(state->md, "value");
+    if (state->mi->parse_int(state->md, state->md->row[0], &val) < 0 || val <= 0)
+      db_error_inv_value_fail(state->md, "value");
+    iter->full_rows[i].user_id = val;
+    copy_saved_row(state, &iter->full_rows[i].login_row);
+    if (i > 0) fprintf(uid_f, ", ");
+    fprintf(uid_f, "%d", val);
+  }
+  fclose(uid_f); uid_f = NULL;
+  state->mi->free_res(state->md);
+
+  if (!uid_t && !*uid_t) {
+    state->mi->free_res(state->md);
+    return (ptr_iterator_t) iter;
+  }
+
+  if (state->mi->fquery(state->md, USER_INFO_WIDTH,
+                "SELECT * FROM %susers WHERE contest_id = %d AND user_id IN (%s) ORDER BY user_id ;",
+                        state->md->table_prefix, contest_id, uid_t) < 0)
+    goto fail;
+  j = 0;
+  for (i = 0; i < state->md->row_count; i++) {
+    if (!(state->md->row = mysql_fetch_row(state->md->res)))
+      db_error_no_data_fail(state->md);
+    state->md->lengths = mysql_fetch_lengths(state->md->res);
+    if (!state->md->lengths[0])
+      db_error_inv_value_fail(state->md, "value");
+    if (state->mi->parse_int(state->md, state->md->row[0], &val) < 0 || val <= 0)
+      db_error_inv_value_fail(state->md, "value");
+    while (j < iter->total_ids && iter->full_rows[j].user_id < val) j++;
+    if (j < iter->total_ids && iter->full_rows[j].user_id == val) {
+      copy_saved_row(state, &iter->full_rows[j].user_info_row);
+    }
+  }
+  state->mi->free_res(state->md);
+
+  if (state->mi->fquery(state->md, CNTSREG_WIDTH,
+                        "SELECT * FROM %scntsregs WHERE contest_id = %d AND user_id IN (%s) ORDER BY user_id ;",
+                        state->md->table_prefix, contest_id, uid_t) < 0)
+    goto fail;
+  xfree(uid_t); uid_t = 0; uid_z = 0;
+  j = 0;
+  for (i = 0; i < state->md->row_count; i++) {
+    if (!(state->md->row = mysql_fetch_row(state->md->res)))
+      db_error_no_data_fail(state->md);
+    state->md->lengths = mysql_fetch_lengths(state->md->res);
+    if (!state->md->lengths[0])
+      db_error_inv_value_fail(state->md, "value");
+    if (state->mi->parse_int(state->md, state->md->row[0], &val) < 0 || val <= 0)
+      db_error_inv_value_fail(state->md, "value");
+    while (j < iter->total_ids && iter->full_rows[j].user_id < val) j++;
+    if (j < iter->total_ids && iter->full_rows[j].user_id == val) {
+      copy_saved_row(state, &iter->full_rows[j].cntsreg_row);
+    }
+  }
+  state->mi->free_res(state->md);
+
+  return (ptr_iterator_t) iter;
+
+ fail:
+  if (uid_f) {
+    fclose(uid_f);
+    uid_f = 0;
+  }
+  xfree(uid_t); uid_t = 0;
+  state->mi->free_res(state->md);
+  brief_list_iterator_destroy_func((ptr_iterator_t) iter);
+  return 0;
+}
+
+static int
+get_user_count_func(
+        void *data,
+        int contest_id,
+        int group_id,
+        const unsigned char *filter,
+        long long *p_count)
+{
+  struct uldb_mysql_state *state = (struct uldb_mysql_state*) data;
+  unsigned char cmdbuf[1024];
+  int cmdlen, count = 0;
+
+  if (contest_id > 0 && group_id > 0) {
+    // FIXME:
+    abort();
+  } else if (contest_id > 0) {
+    snprintf(cmdbuf, sizeof(cmdbuf), "SELECT COUNT(%slogins.user_id) FROM %slogins, %scntsregs WHERE %slogins.user_id = %scntsregs.user_id AND %scntsregs.contest_id = %d;",
+             state->md->table_prefix, state->md->table_prefix,
+             state->md->table_prefix, state->md->table_prefix,
+             state->md->table_prefix, state->md->table_prefix, contest_id);
+  } else if (group_id > 0) {
+  } else {
+    snprintf(cmdbuf, sizeof(cmdbuf), "SELECT COUNT(user_id) FROM %slogins WHERE 1 ;", state->md->table_prefix);
+  }
+
+  cmdlen = strlen(cmdbuf);
+  if (state->mi->query_one_row(state->md, cmdbuf, cmdlen, 1) < 0) goto fail;
+  if (!state->md->lengths[0])
+    db_error_inv_value_fail(state->md, "value");
+  if (state->mi->parse_int(state->md, state->md->row[0], &count) < 0 || count <= 0)
+    db_error_inv_value_fail(state->md, "value");
+  state->mi->free_res(state->md);
+  if (p_count) *p_count = count;
+  return 0;
+
+fail:
+  state->mi->free_res(state->md);
+  return 0;
+}
+
+static ptr_iterator_t
+get_group_iterator_2_func(
+        void *data,
+        const unsigned char *filter,
+        int offset,
+        int count)
+{
+  struct uldb_mysql_state *state = (struct uldb_mysql_state*) data;
+  char *cmd_t = 0;
+  size_t cmd_z = 0;
+  FILE *cmd_f = 0;
+  struct group_iterator *iter = 0;
+  int i;
+
+  if (offset < 0) offset = 0;
+  if (count < 0) count = 0;
+
+  XCALLOC(iter, 1);
+  iter->b = group_iterator_funcs;
+
+  cmd_f = open_memstream(&cmd_t, &cmd_z);
+  fprintf(cmd_f, "SELECT * FROM %sgroups WHERE 1 ORDER BY group_id LIMIT %d, %d;",
+          state->md->table_prefix, offset, count);
+  fclose(cmd_f); cmd_f = 0;
+  if (state->mi->query(state->md, cmd_t, cmd_z, USERGROUP_WIDTH) < 0)
+    goto fail;
+  xfree(cmd_t); cmd_t = 0; cmd_z = 0;
+  iter->group_count = state->md->row_count;
+  if (iter->group_count <= 0) {
+    state->mi->free_res(state->md);
+    return (ptr_iterator_t) iter;
+  }
+  XCALLOC(iter->groups, iter->group_count);
+  for (i = 0; i < iter->group_count; ++i) {
+    if (!(state->md->row = mysql_fetch_row(state->md->res)))
+      db_error_no_data_fail(state->md);
+    state->md->lengths = mysql_fetch_lengths(state->md->res);
+    iter->groups[i] = (struct userlist_group*) userlist_node_alloc(USERLIST_T_USERGROUP);
+    if (parse_group(state, state->md->field_count, state->md->row,
+                    state->md->lengths, iter->groups[i]) < 0) goto fail;
+  }
+
+  state->mi->free_res(state->md);
+  return (ptr_iterator_t) iter;
+
+fail:
+  state->mi->free_res(state->md);
+  group_iterator_destroy_func((ptr_iterator_t) iter);
+  if (cmd_f) fclose(cmd_f);
+  xfree(cmd_t);
+  return 0;
+}
+
+static int
+get_group_count_func(
+        void *data,
+        const unsigned char *filter,
+        long long *p_count)
+{
+  struct uldb_mysql_state *state = (struct uldb_mysql_state*) data;
+  unsigned char cmdbuf[1024];
+  int cmdlen, count = 0;
+
+  snprintf(cmdbuf, sizeof(cmdbuf), "SELECT COUNT(group_id) FROM %sgroups WHERE 1 ;", state->md->table_prefix);
+
+  cmdlen = strlen(cmdbuf);
+  if (state->mi->query_one_row(state->md, cmdbuf, cmdlen, 1) < 0) goto fail;
+  if (!state->md->lengths[0])
+    db_error_inv_value_fail(state->md, "value");
+  if (state->mi->parse_int(state->md, state->md->row[0], &count) < 0 || count <= 0)
+    db_error_inv_value_fail(state->md, "value");
+  state->mi->free_res(state->md);
+  if (p_count) *p_count = count;
+  return 0;
+
+fail:
+  state->mi->free_res(state->md);
+  return 0;
+}
+
+static int
+get_prev_user_id_func(
+        void *data,
+        int contest_id,
+        int group_id,
+        int user_id,
+        const unsigned char *filter,
+        int *p_user_id)
+{
+  struct uldb_mysql_state *state = (struct uldb_mysql_state*) data;
+  unsigned char cmdbuf[1024];
+  int cmdlen;
+
+  if (contest_id > 0 && group_id > 0) group_id = 0; // FIXME
+  if (group_id > 0) group_id = 0;                   /* FIXME */
+  if (p_user_id) *p_user_id = 0;
+
+  if (contest_id > 0 && group_id > 0) {
+    abort();
+  } else if (contest_id > 0) {
+    snprintf(cmdbuf, sizeof(cmdbuf), "SELECT %slogins.user_id FROM %slogins, %scntsregs WHERE %slogins.user_id = %scntsregs.user_id AND %scntsregs.contest_id = %d AND %slogins.user_id < %d ORDER BY %slogins.user_id DESC LIMIT 0, 1;",
+             state->md->table_prefix, state->md->table_prefix,
+             state->md->table_prefix, state->md->table_prefix,
+             state->md->table_prefix, state->md->table_prefix, contest_id,
+             state->md->table_prefix, user_id,
+             state->md->table_prefix);
+  } else if (group_id > 0) {
+    abort();
+  } else {
+    snprintf(cmdbuf, sizeof(cmdbuf), "SELECT user_id FROM %slogins WHERE user_id < %d ORDER BY user_id DESC LIMIT 0, 1;",
+             state->md->table_prefix, user_id);
+  }
+  cmdlen = strlen(cmdbuf);
+  if (state->mi->query(state->md, cmdbuf, cmdlen, 1) < 0) goto fail;
+  if (state->md->row_count != 1) goto fail;
+  if (!(state->md->row = mysql_fetch_row(state->md->res))) goto fail;
+  state->md->lengths = mysql_fetch_lengths(state->md->res);
+  if (!state->md->lengths[0]) goto fail;
+  if (state->mi->parse_int(state->md, state->md->row[0], &user_id) < 0 || user_id <= 0) goto fail;
+  if (p_user_id) *p_user_id = user_id;
+
+fail:
+  state->mi->free_res(state->md);
+  return 0;
+}
+
+static int
+get_next_user_id_func(
+        void *data,
+        int contest_id,
+        int group_id,
+        int user_id,
+        const unsigned char *filter,
+        int *p_user_id)
+{
+  struct uldb_mysql_state *state = (struct uldb_mysql_state*) data;
+  unsigned char cmdbuf[1024];
+  int cmdlen;
+
+  if (contest_id > 0 && group_id > 0) group_id = 0; // FIXME
+  if (group_id > 0) group_id = 0;                   /* FIXME */
+  if (p_user_id) *p_user_id = 0;
+
+  if (contest_id > 0 && group_id > 0) {
+    abort();
+  } else if (contest_id > 0) {
+    snprintf(cmdbuf, sizeof(cmdbuf), "SELECT %slogins.user_id FROM %slogins, %scntsregs WHERE %slogins.user_id = %scntsregs.user_id AND %scntsregs.contest_id = %d AND %slogins.user_id > %d ORDER BY %slogins.user_id LIMIT 0, 1;",
+             state->md->table_prefix, state->md->table_prefix,
+             state->md->table_prefix, state->md->table_prefix,
+             state->md->table_prefix, state->md->table_prefix, contest_id,
+             state->md->table_prefix, user_id,
+             state->md->table_prefix);
+  } else if (group_id > 0) {
+    abort();
+  } else {
+    snprintf(cmdbuf, sizeof(cmdbuf), "SELECT user_id FROM %slogins WHERE user_id > %d ORDER BY user_id DESC LIMIT 0, 1;",
+             state->md->table_prefix, user_id);
+  }
+  cmdlen = strlen(cmdbuf);
+  if (state->mi->query(state->md, cmdbuf, cmdlen, 1) < 0) goto fail;
+  if (state->md->row_count != 1) goto fail;
+  if (!(state->md->row = mysql_fetch_row(state->md->res))) goto fail;
+  state->md->lengths = mysql_fetch_lengths(state->md->res);
+  if (!state->md->lengths[0]) goto fail;
+  if (state->mi->parse_int(state->md, state->md->row[0], &user_id) < 0 || user_id <= 0) goto fail;
+  if (p_user_id) *p_user_id = user_id;
+
+fail:
+  state->mi->free_res(state->md);
+  return 0;
+}
+
 /*
  * Local variables:
  *  compile-command: "make"
diff -ruNp ejudge-2.3.18/prepare.c ejudge/prepare.c
--- ejudge-2.3.18/prepare.c	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/prepare.c	2011-07-11 09:02:04.546273344 +0400
@@ -1,5 +1,5 @@
 /* -*- c -*- */
-/* $Id: prepare.c 6229 2011-04-07 08:49:11Z cher $ */
+/* $Id: prepare.c 6345 2011-05-23 04:20:49Z cher $ */
 
 /* Copyright (C) 2000-2011 Alexander Chernov <cher@ejudge.ru> */
 
@@ -3058,7 +3058,7 @@ set_defaults(
       }
     }
 
-    if (lang->style_checker_cmd[0] && lang->style_checker_cmd[0] != '@') {
+    if (lang->style_checker_cmd[0] && lang->style_checker_cmd[0] != '@' && lang->style_checker_cmd[0] != '%') {
       pathmake2(lang->style_checker_cmd, g->ejudge_checkers_dir,
                 "/", lang->style_checker_cmd, NULL);
     }
diff -ruNp ejudge-2.3.18/prepare_meta.c ejudge/prepare_meta.c
--- ejudge-2.3.18/prepare_meta.c	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/prepare_meta.c	2011-07-11 09:02:04.112281322 +0400
@@ -1,5 +1,5 @@
 // This is an auto-generated file, do not edit
-// Generated 2011/04/02 00:03:17
+// Generated 2011/04/30 20:02:37
 
 #include "prepare_meta.h"
 #include "prepare.h"
@@ -316,6 +316,8 @@ int cntsglob_lookup_field(const char *na
 
 const struct meta_methods cntsglob_methods =
 {
+  CNTSGLOB_LAST_FIELD,
+  sizeof(struct section_global_data),
   cntsglob_get_type,
   cntsglob_get_size,
   cntsglob_get_name,
@@ -519,6 +521,8 @@ int cntsprob_lookup_field(const char *na
 
 const struct meta_methods cntsprob_methods =
 {
+  CNTSPROB_LAST_FIELD,
+  sizeof(struct section_problem_data),
   cntsprob_get_type,
   cntsprob_get_size,
   cntsprob_get_name,
@@ -604,6 +608,8 @@ int cntslang_lookup_field(const char *na
 
 const struct meta_methods cntslang_methods =
 {
+  CNTSLANG_LAST_FIELD,
+  sizeof(struct section_language_data),
   cntslang_get_type,
   cntslang_get_size,
   cntslang_get_name,
@@ -702,6 +708,8 @@ int cntstester_lookup_field(const char *
 
 const struct meta_methods cntstester_methods =
 {
+  CNTSTESTER_LAST_FIELD,
+  sizeof(struct section_tester_data),
   cntstester_get_type,
   cntstester_get_size,
   cntstester_get_name,
diff -ruNp ejudge-2.3.18/prepare_meta.h ejudge/prepare_meta.h
--- ejudge-2.3.18/prepare_meta.h	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/prepare_meta.h	2011-07-11 09:02:04.142280772 +0400
@@ -1,5 +1,5 @@
 // This is an auto-generated file, do not edit
-// Generated 2011/04/02 00:03:17
+// Generated 2011/04/30 20:02:37
 
 #ifndef __PREPARE_META_H__
 #define __PREPARE_META_H__
diff -ruNp ejudge-2.3.18/prepare_out.c ejudge/prepare_out.c
--- ejudge-2.3.18/prepare_out.c	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/prepare_out.c	2011-07-11 09:02:04.096281617 +0400
@@ -1,5 +1,5 @@
 /* -*- mode: c -*- */
-/* $Id: prepare_out.c 6214 2011-04-01 20:03:14Z cher $ */
+/* $Id: prepare_out.c 6246 2011-04-15 04:37:15Z cher $ */
 
 /* Copyright (C) 2005-2011 Alexander Chernov <cher@ejudge.ru> */
 
@@ -61,31 +61,6 @@ unparse_bool(FILE *f, const unsigned cha
   fprintf(f, "%s%s\n", name, value?"":" = 0");
 }
 
-#define SIZE_G (1024 * 1024 * 1024)
-#define SIZE_M (1024 * 1024)
-#define SIZE_K (1024)
-
-static unsigned char*
-num_to_size(unsigned char *buf, size_t buf_size, int num)
-{
-  if (!num) snprintf(buf, buf_size, "0");
-  else if (!(num % SIZE_G)) snprintf(buf, buf_size, "%uG", num / SIZE_G);
-  else if (!(num % SIZE_M)) snprintf(buf, buf_size, "%uM", num / SIZE_M);
-  else if (!(num % SIZE_K)) snprintf(buf, buf_size, "%uK", num / SIZE_K);
-  else snprintf(buf, buf_size, "%u", num);
-  return buf;
-}
-static unsigned char*
-size_t_to_size(unsigned char *buf, size_t buf_size, size_t num)
-{
-  if (!num) snprintf(buf, buf_size, "0");
-  else if (!(num % SIZE_G)) snprintf(buf, buf_size, "%zuG", num / SIZE_G);
-  else if (!(num % SIZE_M)) snprintf(buf, buf_size, "%zuM", num / SIZE_M);
-  else if (!(num % SIZE_K)) snprintf(buf, buf_size, "%zuK", num / SIZE_K);
-  else snprintf(buf, buf_size, "%zu", num);
-  return buf;
-}
-
 static void
 do_str(
         FILE *f,
@@ -315,18 +290,18 @@ prepare_unparse_global(FILE *f, struct s
 
   if (global->max_run_size != DFLT_G_MAX_RUN_SIZE)
     fprintf(f, "max_run_size = %s\n",
-            num_to_size(nbuf, sizeof(nbuf), global->max_run_size));
+            num_to_size_str(nbuf, sizeof(nbuf), global->max_run_size));
   if (global->max_run_total != DFLT_G_MAX_RUN_TOTAL)
     fprintf(f, "max_run_total = %s\n",
-            num_to_size(nbuf, sizeof(nbuf), global->max_run_total));
+            num_to_size_str(nbuf, sizeof(nbuf), global->max_run_total));
   if (global->max_run_num != DFLT_G_MAX_RUN_NUM)
     fprintf(f, "max_run_num = %d\n", global->max_run_num);
   if (global->max_clar_size != DFLT_G_MAX_CLAR_SIZE)
     fprintf(f, "max_clar_size = %s\n",
-            num_to_size(nbuf, sizeof(nbuf), global->max_clar_size));
+            num_to_size_str(nbuf, sizeof(nbuf), global->max_clar_size));
   if (global->max_clar_total != DFLT_G_MAX_CLAR_TOTAL)
     fprintf(f, "max_clar_total = %s\n",
-            num_to_size(nbuf, sizeof(nbuf), global->max_clar_total));
+            num_to_size_str(nbuf, sizeof(nbuf), global->max_clar_total));
   if (global->max_clar_num != DFLT_G_MAX_CLAR_NUM)
     fprintf(f, "max_clar_num = %d\n", global->max_clar_num);
   if (global->team_page_quota != DFLT_G_TEAM_PAGE_QUOTA)
@@ -334,17 +309,17 @@ prepare_unparse_global(FILE *f, struct s
 
   if (((ssize_t) global->compile_max_vm_size) > 0) {
     fprintf(f, "compile_max_vm_size = %s\n",
-            size_t_to_size(size_buf, sizeof(size_buf),
+            size_t_to_size_str(size_buf, sizeof(size_buf),
                            global->compile_max_vm_size));
   }
   if (((ssize_t) global->compile_max_stack_size) > 0) {
     fprintf(f, "compile_max_stack_size = %s\n",
-            size_t_to_size(size_buf, sizeof(size_buf),
+            size_t_to_size_str(size_buf, sizeof(size_buf),
                            global->compile_max_stack_size));
   }
   if (((ssize_t) global->compile_max_file_size) > 0) {
     fprintf(f, "compile_max_file_size = %s\n",
-            size_t_to_size(size_buf, sizeof(size_buf),
+            size_t_to_size_str(size_buf, sizeof(size_buf),
                            global->compile_max_file_size));
   }
 
@@ -486,10 +461,10 @@ prepare_unparse_global(FILE *f, struct s
     fprintf(f, "rounding_mode = %s\n", rounding_modes[global->rounding_mode]);
   if (global->max_file_length && global->max_file_length != DFLT_G_MAX_FILE_LENGTH)
     fprintf(f, "max_file_length = %s\n",
-            num_to_size(nbuf, sizeof(nbuf), global->max_file_length));
+            num_to_size_str(nbuf, sizeof(nbuf), global->max_file_length));
   if (global->max_line_length && global->max_line_length != DFLT_G_MAX_LINE_LENGTH)
     fprintf(f, "max_line_length = %s\n",
-            num_to_size(nbuf, sizeof(nbuf), global->max_line_length));
+            num_to_size_str(nbuf, sizeof(nbuf), global->max_line_length));
   if (global->disable_auto_testing != DFLT_G_DISABLE_AUTO_TESTING)
     unparse_bool(f, "disable_auto_testing", global->disable_auto_testing);
   if (global->disable_testing != DFLT_G_DISABLE_TESTING)
@@ -877,15 +852,15 @@ prepare_unparse_lang(
 
   if (lang->max_vm_size != -1L && lang->max_vm_size != 0) {
     fprintf(f, "max_vm_size = %s\n",
-            size_t_to_size(size_buf, sizeof(size_buf), lang->max_vm_size));
+            size_t_to_size_str(size_buf, sizeof(size_buf), lang->max_vm_size));
   }
   if (lang->max_stack_size != -1L && lang->max_stack_size != 0) {
     fprintf(f, "max_stack_size = %s\n",
-            size_t_to_size(size_buf, sizeof(size_buf), lang->max_stack_size));
+            size_t_to_size_str(size_buf, sizeof(size_buf), lang->max_stack_size));
   }
   if (lang->max_file_size != -1L && lang->max_file_size != 0) {
     fprintf(f, "max_file_size = %s\n",
-            size_t_to_size(size_buf, sizeof(size_buf), lang->max_file_size));
+            size_t_to_size_str(size_buf, sizeof(size_buf), lang->max_file_size));
   }
 
   if (lang->compiler_env) {
@@ -1131,19 +1106,19 @@ prepare_unparse_prob(
 
   if (prob->max_vm_size != -1L)
     fprintf(f, "max_vm_size = %s\n",
-            size_t_to_size(size_buf, sizeof(size_buf), prob->max_vm_size));
+            size_t_to_size_str(size_buf, sizeof(size_buf), prob->max_vm_size));
   if (prob->max_stack_size != -1L)
     fprintf(f, "max_stack_size = %s\n",
-            size_t_to_size(size_buf, sizeof(size_buf), prob->max_stack_size));
+            size_t_to_size_str(size_buf, sizeof(size_buf), prob->max_stack_size));
   if (prob->max_data_size != -1L)
     fprintf(f, "max_data_size = %s\n",
-            size_t_to_size(size_buf, sizeof(size_buf), prob->max_data_size));
+            size_t_to_size_str(size_buf, sizeof(size_buf), prob->max_data_size));
   if (prob->max_core_size != -1L)
     fprintf(f, "max_core_size = %s\n",
-            size_t_to_size(size_buf, sizeof(size_buf), prob->max_core_size));
+            size_t_to_size_str(size_buf, sizeof(size_buf), prob->max_core_size));
   if (prob->max_file_size != -1L)
     fprintf(f, "max_file_size = %s\n",
-            size_t_to_size(size_buf, sizeof(size_buf), prob->max_file_size));
+            size_t_to_size_str(size_buf, sizeof(size_buf), prob->max_file_size));
   if (prob->max_open_file_count >= 0) {
     fprintf(f, "max_open_file_count = %d\n", prob->max_open_file_count);
   }
diff -ruNp ejudge-2.3.18/scripts/ghc.in ejudge/scripts/ghc.in
--- ejudge-2.3.18/scripts/ghc.in	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/scripts/ghc.in	2011-07-11 09:01:58.308387855 +0400
@@ -0,0 +1,37 @@
+#!/bin/sh
+# $Id: ghc.in 6385 2011-06-30 05:21:01Z cher $
+# Copyright (c) 2011 Alexander Chernov <cher@ejudge.ru>
+
+# using EJUDGE_FLAGS we may pass additional flags
+
+LANG_CONFIG_DIR="@lang_config_dir@"
+[ "${EJUDGE_LANG_CONFIG}" = "" ] && EJUDGE_LANG_CONFIG="${LANG_CONFIG_DIR}/ghc.cfg"
+
+if [ -f "${EJUDGE_LANG_CONFIG}" ]
+then
+  . "${EJUDGE_LANG_CONFIG}"
+else
+  version="unknown"
+  GHCRUN="ghc"
+  GHCDIR=
+fi
+
+if [ x"${version}" = x ]
+then
+  echo "This language is not supported." >&2
+  exit 1
+fi
+
+if [ x"${GHCDIR}" != x ]
+then
+    PATH="${GHCDIR}:${PATH}"
+fi
+
+[ x"${EJUDGE_FLAGS}" = x ] && EJUDGE_FLAGS="-O -H14m"
+
+unset LANG
+unset LC_ALL
+unset LC_MESSAGES
+unset LANGUAGE
+
+exec "${GHCRUN}" ${EJUDGE_FLAGS} "$1" -o "$2"
diff -ruNp ejudge-2.3.18/scripts/ghc-version.in ejudge/scripts/ghc-version.in
--- ejudge-2.3.18/scripts/ghc-version.in	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/scripts/ghc-version.in	2011-07-11 09:01:58.327387506 +0400
@@ -0,0 +1,117 @@
+#!/bin/sh
+# $Id: ghc-version.in 6385 2011-06-30 05:21:01Z cher $
+# Copyright (c) 2011 Alexander Chernov <cher@ejudge.ru>
+
+# compiler script interface is as follows
+# <comp>-version [OPTION]
+# OPTION is:
+#  -r P fully check the availability of the compiler (like configure
+#       script does). P is a proposed path to the compiler (like in
+#       --with-<comp> configure option).
+#       returns failure (exit code 1), if the compiler is not available
+#       or is not functional.
+#       in case of success exit code is 0, and the necessary configuration
+#       variables are written to stdout.
+#  -v   verbose operation (configure mode)
+#  -l   output the compiler name to the stdout (list mode)
+#  -p   write to the stdout the command line of the compiler
+#  -f   write the compiler full name and the compiler version
+
+LANG_CONFIG_DIR="@lang_config_dir@"
+
+common_config() {
+  echo 'long_name="The Glasgow Haskell Compiler"'
+  echo 'src_sfx=".hs"'
+  echo 'arch="linux-shared"'
+  echo 'insecure=1'
+}
+
+failure() {
+  rm -f conftest*
+  echo 'version='
+  echo 'arg="'"${arg}"'"'
+  common_config
+  echo 'GHCRUN=false'
+  echo 'GHCDIR='
+  [ "${verbose}" = 1 ] && echo "no" >&2
+  exit 1
+}
+
+unset LANG
+unset LC_ALL
+unset LC_MESSAGES
+unset LANGUAGE
+
+if [ x"$1" = x-v ]
+then
+  verbose=1
+  shift
+fi
+
+# recheck the language
+if [ x"$1" = x-r ]
+then
+  arg="$2"
+  [ x"$2" != x ] && GHCRUN="$2"
+  [ "${GHCRUN}" = "" ] && GHCRUN="ghc"
+
+  [ "${verbose}" = 1 ] && echo -n "checking whether GHC is available... " >&2
+  "${GHCRUN}" --version >/dev/null 2>&1 || failure
+  version=`"${GHCRUN}" --version 2>&1 | grep "version" | gawk '{ print $8; }'`|| failure
+  cat > conftest.hs <<EOF
+main = putStrLn "ok"
+EOF
+  "${GHCRUN}" conftest.hs -o conftest >/dev/null 2>&1 || failure
+  [ -x conftest ] || failure
+  ./conftest >/dev/null 2>&1 || failure
+
+  rm -f ./conftest*
+  GHCDIR=`dirname "${GHCRUN}"`
+  [ "${GHCDIR}" = . ] && GHCDIR=""
+  echo 'version="'"${version}"'"'
+  echo 'arg="'"${arg}"'"'
+  common_config
+  echo 'GHCRUN="'"${GHCRUN}"'"'
+  echo 'GHCDIR="'"${GHCDIR}"'"'
+  [ "${verbose}" = 1 ] && echo "yes, ${GHCRUN}, ${version}" >&2
+  exit 0
+fi
+
+if [ x"$1" = x-l ]
+then
+  echo "The Glasgow Haskell [http://haskell.org/ghc]"
+  exit 0
+fi
+
+[ "${EJUDGE_LANG_CONFIG}" = "" ] && EJUDGE_LANG_CONFIG="${LANG_CONFIG_DIR}/ghc.cfg"
+
+if [ -f "${EJUDGE_LANG_CONFIG}" ]
+then
+  . "${EJUDGE_LANG_CONFIG}"
+else
+  GHCRUN="ghc"
+  GHCDIR=
+fi
+
+if [ x"${GHCRUN}" = x -o x"${GHCRUN}" = xfalse ]
+then
+  echo "This language is not supported." >&2
+  exit 1
+fi
+
+if [ x"${GHCDIR}" != x ]
+then
+    PATH="${GHCDIR}:${PATH}"
+fi
+
+if [ x"$1" = x-p ]
+then
+    echo "${GHCRUN}"
+    exit 0
+fi
+    
+"${GHCRUN}" --version >/dev/null 2>&1 || exit 1
+
+[ x"$1" = x-f ] && echo -n "The Glasgow Haskell Compiler "
+
+"${GHCRUN}" --version 2>&1 | grep "version" | gawk '{ print $8; }'
diff -ruNp ejudge-2.3.18/scripts/lang_ids.cfg ejudge/scripts/lang_ids.cfg
--- ejudge-2.3.18/scripts/lang_ids.cfg	2011-04-10 09:25:16.000000000 +0400
+++ ejudge/scripts/lang_ids.cfg	2011-07-11 09:01:58.291388167 +0400
@@ -1,4 +1,4 @@
-# $Id: lang_ids.cfg 6041 2010-11-17 22:03:39Z cher $
+# $Id: lang_ids.cfg 6385 2011-06-30 05:21:01Z cher $
 # 
 # This is language id registry. The id is assigned to each supported
 # language. If the several languages has the same id, the latter
@@ -31,3 +31,5 @@ ruby=21
 php=22
 python3=23
 kumir=24
+make=25
+ghc=26
diff -ruNp ejudge-2.3.18/scripts/Makefile.in ejudge/scripts/Makefile.in
--- ejudge-2.3.18/scripts/Makefile.in	2011-04-10 09:25:15.000000000 +0400
+++ ejudge/scripts/Makefile.in	2011-07-11 09:01:58.229389304 +0400
@@ -1,8 +1,8 @@
 # -*- Makefile -*-
-# $Id: Makefile.in 6047 2010-11-18 12:24:38Z cher $
+# $Id: Makefile.in 6385 2011-06-30 05:21:01Z cher $
 # @configure_input@
 
-# Copyright (C) 2004-2008 Alexander Chernov <cher@ejudge.ru> */
+# Copyright (C) 2004-2011 Alexander Chernov <cher@ejudge.ru> */
 
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -102,6 +102,10 @@ SCRIPTS_IN = \
  mcs-version.in\
  vbnc.in\
  vbnc-version.in\
+ make.in\
+ make-version.in\
+ ghc.in\
+ ghc-version.in\
  runjava.in\
  runmono.in\
  runperl.in
diff -ruNp ejudge-2.3.18/scripts/make.in ejudge/scripts/make.in
--- ejudge-2.3.18/scripts/make.in	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/scripts/make.in	2011-07-11 09:01:58.231389267 +0400
@@ -0,0 +1,115 @@
+#!/bin/sh
+# $Id: make.in 6385 2011-06-30 05:21:01Z cher $
+# Copyright (c) 2011 Alexander Chernov <cher@ejudge.ru>
+
+# Environment variables:
+#  EJ_MAKE_COMMAND
+#  EJ_MAKE_ARGS
+#  EJ_PROGRAM_DIR
+#  EJ_PROGRAM_NAME
+
+srcfile="$1"
+dstfile="$2"
+
+LANG_CONFIG_DIR="@lang_config_dir@"
+[ "${EJUDGE_LANG_CONFIG}" = "" ] && EJUDGE_LANG_CONFIG="${LANG_CONFIG_DIR}/make.cfg"
+
+if [ -f "${EJUDGE_LANG_CONFIG}" ]
+then
+  . "${EJUDGE_LANG_CONFIG}"
+else
+  version="generic"
+  MAKERUN="make"
+fi
+
+[ x"${EJ_MAKE_COMMAND}" != x ] || EJ_MAKE_COMMAND="make"
+[ x"${EJ_PROGRAM_DIR}" != x ] || EJ_PROGRAM_DIR="solution"
+[ x"${EJ_PROGRAM_NAME}" != x ] || EJ_PROGRAM_NAME="solution"
+
+# 1. detect the type of the archive
+#  7z:  "7-zip archive data"
+#  tar: "POSIX tar archive (GNU)"
+#  bz2: "bzip2 compressed data"
+#  gz:  "gzip compressed data"
+if [ ! -f "${srcfile}" ]
+then
+  echo "No source file ${srcfile}" 1>&2
+  exit 1
+fi
+
+t=`file "${srcfile}"`
+case "$t" in
+    *7-zip\ archive\ data*)
+        cmd="7z x";;
+    *POSIX\ tar\ archive*)
+        cmd="tar xf";;
+    *bzip2\ compressed\ data*)
+        cmd="tar xfj";;
+    *gzip\ compressed\ data*)
+        cmd="tar xfz";;
+    *)
+        echo "Unsupported file type ${t}" 1>&2
+        exit 1;;
+esac
+if [ x"${cmd}" = x ]
+then
+    echo "Unsupported file type ${t}" 1>&2
+    exit 1
+fi
+
+# 2. unpack the archive
+${cmd} "${srcfile}"
+if [ $? != 0 ]
+then
+    echo "Failed to uncompress the archive" 1>&2
+    exit 1
+fi
+if [ ! -d "${EJ_PROGRAM_DIR}" ]
+then
+    echo "The archive does not contain directory ${EJ_PROGRAM_DIR}" 1>&2
+    exit 1
+fi
+
+# 3. execute
+curdir=`pwd`
+if [ x"${curdir}" = x ]
+then
+    echo "Invalid current directory" 1>&2
+    exit 1
+fi
+
+if ! cd "${EJ_PROGRAM_DIR}"
+then
+    echo "Failed to change the current directory to ${EJ_PROGRAM_DIR}" 1>&2
+    exit 1
+fi
+
+${EJ_MAKE_COMMAND} ${EJ_MAKE_ARGS}
+if [ $? != 0 ]
+then
+    echo "Build failed" 1>&2
+    exit 1
+fi
+
+if [ ! -x "${EJ_PROGRAM_NAME}" ]
+then
+    echo "The executable file is not created or non-existable" 1>&2
+    exit 1
+fi
+
+# 4. copy the result
+cd "${curdir}"
+if [ $? != 0 ]
+then
+    echo "Invalid current directory" 1>&2
+    exit 1
+fi
+
+mv "${EJ_PROGRAM_DIR}"/"${EJ_PROGRAM_NAME}" "${dstfile}"
+if [ $? != 0 ]
+then
+    echo "Failed to move the executable file" 1>&2
+    exit 1
+fi
+
+exit 0
diff -ruNp ejudge-2.3.18/scripts/make-version.in ejudge/scripts/make-version.in
--- ejudge-2.3.18/scripts/make-version.in	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/scripts/make-version.in	2011-07-11 09:01:58.246388991 +0400
@@ -0,0 +1,85 @@
+#!/bin/sh
+# $Id: make-version.in 6385 2011-06-30 05:21:01Z cher $
+# Copyright (c) 2011 Alexander Chernov <cher@ejudge.ru>
+
+# This is a version script for a generic build system.
+# It supports make, ant, scons, etc...
+# Compilation of a submit is directed by several
+# environment variables.
+
+LANG_CONFIG_DIR="@lang_config_dir@"
+
+common_config() {
+  echo 'long_name="Generic build"'
+  echo 'src_sfx=".tar"'
+  echo 'arch="linux-shared"'
+}
+
+failure() {
+  rm -f conftest*
+  echo 'version='
+  echo 'arg="'"${arg}"'"'
+  common_config
+  echo 'MAKEPATH=/bin/false'
+  [ "${verbose}" = 1 ] && echo "no" >&2
+  exit 1
+}
+
+unset LANG
+unset LC_ALL
+unset LC_MESSAGES
+unset LANGUAGE
+
+if [ x"$1" = x-v ]
+then
+  verbose=1
+  shift
+fi
+
+# recheck the language
+if [ x"$1" = x-r ]
+then
+  arg="$2"
+
+  [ "${verbose}" = 1 ] && echo -n "checking whether make is available... " >&2
+
+  version=generic
+  echo 'version="'"${version}"'"'
+  echo 'arg="'"${arg}"'"'
+  echo 'binary=1'
+  common_config
+  echo 'MAKEPATH=/usr/bin/make'
+  [ "${verbose}" = 1 ] && echo "yes, ${version}" >&2
+  exit 0
+fi
+
+if [ x"$1" = x-l ]
+then
+  echo "Generic build"
+  exit 0
+fi
+
+[ "${EJUDGE_LANG_CONFIG}" = "" ] && EJUDGE_LANG_CONFIG="${LANG_CONFIG_DIR}/make.cfg"
+
+if [ -f "${EJUDGE_LANG_CONFIG}" ]
+then
+  . "${EJUDGE_LANG_CONFIG}"
+else
+  MAKEPATH="/usr/bin/make"
+fi
+
+if [ x"${MAKEPATH}" = x -o x"${MAKEPATH}" = x/bin/false ]
+then
+  echo "This language is not supported." >&2
+  exit 1
+fi
+
+if [ x"$1" = x-p ]
+then
+    echo "${MAKEPATH}"
+    exit 0
+fi
+
+[ x"$1" = x-f ] && echo -n "Make "
+
+echo "generic"
diff -ruNp ejudge-2.3.18/serve_2.c ejudge/serve_2.c
--- ejudge-2.3.18/serve_2.c	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/serve_2.c	2011-07-11 09:02:04.120281176 +0400
@@ -1,5 +1,5 @@
 /* -*- mode: c -*- */
-/* $Id: serve_2.c 6229 2011-04-07 08:49:11Z cher $ */
+/* $Id: serve_2.c 6393 2011-07-04 20:54:33Z cher $ */
 
 /* Copyright (C) 2006-2011 Alexander Chernov <cher@ejudge.ru> */
 
@@ -1615,9 +1615,10 @@ serve_read_compile_packet(
   struct compile_reply_packet *comp_pkt = 0;
   long report_size = 0;
   unsigned char errmsg[1024] = { 0 };
-  unsigned char *team_name = 0;
+  //unsigned char *team_name = 0;
   struct compile_run_extra *comp_extra = 0;
   struct section_problem_data *prob = 0;
+  struct section_language_data *lang = 0;
   int arch_flags;
   path_t run_arch_path;
   char *run_text = 0;
@@ -1733,6 +1734,9 @@ serve_read_compile_packet(
     snprintf(errmsg, sizeof(errmsg), "invalid problem %d\n", re.prob_id);
     goto report_check_failed;
   }
+  if (re.lang_id > 1 && re.lang_id <= state->max_lang) {
+    lang = state->langs[re.lang_id];
+  }
   /*
   if (re.lang_id < 1 || re.lang_id > state->max_lang
       || !(lang = state->langs[re.lang_id])) {
@@ -1740,10 +1744,12 @@ serve_read_compile_packet(
     goto report_check_failed;
   }
   */
+  /*
   if (!(team_name = teamdb_get_name(state->teamdb_state, re.user_id))) {
     snprintf(errmsg, sizeof(errmsg), "invalid team %d\n", re.user_id);
     goto report_check_failed;
   }
+  */
   if (prob->disable_testing && prob->enable_compilation > 0) {
     if (run_change_status_4(state->runlog_state, comp_pkt->run_id,
                             RUN_ACCEPTED) < 0)
@@ -1764,13 +1770,12 @@ serve_read_compile_packet(
    * so far compilation is successful, and now we prepare a run packet
    */
 
-  if (prob && prob->type > 0 && prob->style_checker_cmd
-      && prob->style_checker_cmd[0]) {
-    // copy textual representation
+  if ((prob && prob->style_checker_cmd && prob->style_checker_cmd[0])
+      || (lang && lang->style_checker_cmd && lang->style_checker_cmd[0])) {
     snprintf(pkt_name, sizeof(pkt_name), "%06d", comp_pkt->run_id);
-    snprintf(run_arch_path, sizeof(run_arch_path), "%s/%s",
+    snprintf(run_arch_path, sizeof(run_arch_path), "%s/%s.txt",
              compile_report_dir, pkt_name);
-    if (generic_read_file(&run_text, 0, &run_size, 0, 0, run_arch_path, 0)>=0){
+    if (generic_read_file(&run_text, 0, &run_size, 0, 0, run_arch_path, 0) >= 0){
       arch_flags = archive_make_write_path(state,
                                            run_arch_path, sizeof(run_arch_path),
                                            global->report_archive_dir,
@@ -1784,7 +1789,9 @@ serve_read_compile_packet(
       }
       xfree(run_text); run_text = 0; run_size = 0;
     }
+  }
 
+  if (prob && prob->type > 0 && prob->style_checker_cmd && prob->style_checker_cmd[0]) {
     arch_flags = archive_make_read_path(state, run_arch_path,
                                         sizeof(run_arch_path),
                                         state->global->run_archive_dir,
diff -ruNp ejudge-2.3.18/serve-control.c ejudge/serve-control.c
--- ejudge-2.3.18/serve-control.c	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/serve-control.c	2011-07-11 09:02:04.790268858 +0400
@@ -1,5 +1,5 @@
 /* -*- mode: c -*- */
-/* $Id: serve-control.c 6214 2011-04-01 20:03:14Z cher $ */
+/* $Id: serve-control.c 6351 2011-05-24 19:28:45Z cher $ */
 
 /* Copyright (C) 2004-2011 Alexander Chernov <cher@ejudge.ru> */
 
@@ -1498,6 +1498,8 @@ static const int next_action_map[SSERV_C
   [SSERV_CMD_CNTS_CLEAR_TEAM_URL] = SSERV_CMD_EDIT_CURRENT_CONTEST,
   [SSERV_CMD_CNTS_CLEAR_STANDINGS_URL] = SSERV_CMD_EDIT_CURRENT_CONTEST,
   [SSERV_CMD_CNTS_CLEAR_PROBLEMS_URL] = SSERV_CMD_EDIT_CURRENT_CONTEST,
+  [SSERV_CMD_CNTS_CLEAR_LOGO_URL] = SSERV_CMD_EDIT_CURRENT_CONTEST,
+  [SSERV_CMD_CNTS_CLEAR_CSS_URL] = SSERV_CMD_EDIT_CURRENT_CONTEST,
   [SSERV_CMD_CNTS_CLEAR_ROOT_DIR] = SSERV_CMD_EDIT_CURRENT_CONTEST,
   [SSERV_CMD_CNTS_CLEAR_CONF_DIR] = SSERV_CMD_EDIT_CURRENT_CONTEST,
   [SSERV_CMD_CNTS_CLEAR_USERS_HEADER_TEXT] = SSERV_CMD_EDIT_CURRENT_CONTEST,
@@ -1585,6 +1587,8 @@ static const int next_action_map[SSERV_C
   [SSERV_CMD_CNTS_CHANGE_TEAM_URL] = SSERV_CMD_EDIT_CURRENT_CONTEST,
   [SSERV_CMD_CNTS_CHANGE_STANDINGS_URL] = SSERV_CMD_EDIT_CURRENT_CONTEST,
   [SSERV_CMD_CNTS_CHANGE_PROBLEMS_URL] = SSERV_CMD_EDIT_CURRENT_CONTEST,
+  [SSERV_CMD_CNTS_CHANGE_LOGO_URL] = SSERV_CMD_EDIT_CURRENT_CONTEST,
+  [SSERV_CMD_CNTS_CHANGE_CSS_URL] = SSERV_CMD_EDIT_CURRENT_CONTEST,
   [SSERV_CMD_CNTS_CHANGE_ROOT_DIR] = SSERV_CMD_EDIT_CURRENT_CONTEST,
   [SSERV_CMD_CNTS_CHANGE_CONF_DIR] = SSERV_CMD_EDIT_CURRENT_CONTEST,
   [SSERV_CMD_CNTS_CHANGE_DIR_MODE] = SSERV_CMD_EDIT_CURRENT_CONTEST,
@@ -2303,6 +2307,8 @@ main(int argc, char *argv[])
   case SSERV_CMD_CNTS_CHANGE_TEAM_URL:
   case SSERV_CMD_CNTS_CHANGE_STANDINGS_URL:
   case SSERV_CMD_CNTS_CHANGE_PROBLEMS_URL:
+  case SSERV_CMD_CNTS_CHANGE_LOGO_URL:
+  case SSERV_CMD_CNTS_CHANGE_CSS_URL:
   case SSERV_CMD_CNTS_CHANGE_ROOT_DIR:
   case SSERV_CMD_CNTS_CHANGE_CONF_DIR:
   case SSERV_CMD_CNTS_CHANGE_DIR_MODE:
@@ -2431,6 +2437,8 @@ main(int argc, char *argv[])
   case SSERV_CMD_CNTS_CLEAR_TEAM_URL:
   case SSERV_CMD_CNTS_CLEAR_STANDINGS_URL:
   case SSERV_CMD_CNTS_CLEAR_PROBLEMS_URL:
+  case SSERV_CMD_CNTS_CLEAR_LOGO_URL:
+  case SSERV_CMD_CNTS_CLEAR_CSS_URL:
   case SSERV_CMD_CNTS_CLEAR_ROOT_DIR:
   case SSERV_CMD_CNTS_CLEAR_CONF_DIR:
   case SSERV_CMD_CNTS_CLEAR_DIR_MODE:
diff -ruNp ejudge-2.3.18/sformat.c ejudge/sformat.c
--- ejudge-2.3.18/sformat.c	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/sformat.c	2011-07-11 09:02:04.804268600 +0400
@@ -1,5 +1,5 @@
 /* -*- mode: c -*- */
-/* $Id: sformat.c 6185 2011-03-27 15:16:29Z cher $ */
+/* $Id: sformat.c 6345 2011-05-23 04:20:49Z cher $ */
 
 /* Copyright (C) 2001-2011 Alexander Chernov <cher@ejudge.ru> */
 
@@ -37,6 +37,7 @@
 /**
  * Valid format conversions as follows:
  *  G - global data
+ *   Gr - contest root directory
  *  P - problem data
  *   Pi - problem id
  *   Ps - problem short name
@@ -297,8 +298,13 @@ sformat_message(
       /* read specification */
       switch (*pf) {
       case 'G':
+        /*
+         * Gr - contest root directory
+         */
         pf++;
         switch (*pf) {
+        case 'r':
+          break;
         case 0:
           is_invalid = 1;
           break;
@@ -310,6 +316,9 @@ sformat_message(
         if (!is_invalid && !glob_data) is_invalid = 1;
         if (!is_invalid) {
           switch (*pf) {
+          case 'r':
+            papp = glob_data->root_dir;
+            break;
           default:
             abort();
           }
diff -ruNp ejudge-2.3.18/style/ejudge-upgrade-web.in ejudge/style/ejudge-upgrade-web.in
--- ejudge-2.3.18/style/ejudge-upgrade-web.in	2011-04-10 09:25:20.000000000 +0400
+++ ejudge/style/ejudge-upgrade-web.in	2011-07-11 09:02:02.129317756 +0400
@@ -1,6 +1,6 @@
 #! /bin/sh
-# $Id: ejudge-upgrade-web.in 5521 2008-12-28 09:18:13Z cher $
-# Copyright (c) 2008 Alexander Chernov <cher@ejudge.ru>
+# $Id: ejudge-upgrade-web.in 6270 2011-04-27 05:04:59Z cher $
+# Copyright (c) 2008-2011 Alexander Chernov <cher@ejudge.ru>
 
 # This script upgrades the web installation of the ejudge system
 
@@ -107,7 +107,7 @@ then
 fi
 
 # symlink the necessary style files
-for i in logo.gif priv.css unpriv.css priv.js unpriv.js filter_expr.html icons
+for i in logo.gif priv.css unpriv.css priv.js unpriv.js filter_expr.html icons sprintf.js
 do
     if ! ln -sf "${datadir}/ejudge/style/${i}" "${i}"
     then
diff -ruNp ejudge-2.3.18/style/sprintf.js ejudge/style/sprintf.js
--- ejudge-2.3.18/style/sprintf.js	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/style/sprintf.js	2011-07-11 09:02:02.112318069 +0400
@@ -0,0 +1,181 @@
+function sprintf () {
+    // http://kevin.vanzonneveld.net
+    // +   original by: Ash Searle (http://hexmen.com/blog/)
+    // + namespaced by: Michael White (http://getsprink.com)
+    // +    tweaked by: Jack
+    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+    // +      input by: Paulo Freitas
+    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+    // +      input by: Brett Zamir (http://brett-zamir.me)
+    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+    // *     example 1: sprintf("%01.2f", 123.1);
+    // *     returns 1: 123.10
+    // *     example 2: sprintf("[%10s]", 'monkey');
+    // *     returns 2: '[    monkey]'
+    // *     example 3: sprintf("[%'#10s]", 'monkey');
+    // *     returns 3: '[####monkey]'
+    var regex = /%%|%(\d+\$)?([-+\'#0 ]*)(\*\d+\$|\*|\d+)?(\.(\*\d+\$|\*|\d+))?([scboxXuidfegEG])/g;
+    var a = arguments,
+        i = 0,
+        format = a[i++];
+
+    // pad()
+    var pad = function (str, len, chr, leftJustify) {
+        if (!chr) {
+            chr = ' ';
+        }
+        var padding = (str.length >= len) ? '' : Array(1 + len - str.length >>> 0).join(chr);
+        return leftJustify ? str + padding : padding + str;
+    };
+
+    // justify()
+    var justify = function (value, prefix, leftJustify, minWidth, zeroPad, customPadChar) {
+        var diff = minWidth - value.length;
+        if (diff > 0) {
+            if (leftJustify || !zeroPad) {
+                value = pad(value, minWidth, customPadChar, leftJustify);
+            } else {
+                value = value.slice(0, prefix.length) + pad('', diff, '0', true) + value.slice(prefix.length);
+            }
+        }
+        return value;
+    };
+
+    // formatBaseX()
+    var formatBaseX = function (value, base, prefix, leftJustify, minWidth, precision, zeroPad) {
+        // Note: casts negative numbers to positive ones
+        var number = value >>> 0;
+        prefix = prefix && number && {
+            '2': '0b',
+            '8': '0',
+            '16': '0x'
+        }[base] || '';
+        value = prefix + pad(number.toString(base), precision || 0, '0', false);
+        return justify(value, prefix, leftJustify, minWidth, zeroPad);
+    };
+
+    // formatString()
+    var formatString = function (value, leftJustify, minWidth, precision, zeroPad, customPadChar) {
+        if (precision != null) {
+            value = value.slice(0, precision);
+        }
+        return justify(value, '', leftJustify, minWidth, zeroPad, customPadChar);
+    };
+
+    // doFormat()
+    var doFormat = function (substring, valueIndex, flags, minWidth, _, precision, type) {
+        var number;
+        var prefix;
+        var method;
+        var textTransform;
+        var value;
+
+        if (substring == '%%') {
+            return '%';
+        }
+
+        // parse flags
+        var leftJustify = false,
+            positivePrefix = '',
+            zeroPad = false,
+            prefixBaseX = false,
+            customPadChar = ' ';
+        var flagsl = flags.length;
+        for (var j = 0; flags && j < flagsl; j++) {
+            switch (flags.charAt(j)) {
+            case ' ':
+                positivePrefix = ' ';
+                break;
+            case '+':
+                positivePrefix = '+';
+                break;
+            case '-':
+                leftJustify = true;
+                break;
+            case "'":
+                customPadChar = flags.charAt(j + 1);
+                break;
+            case '0':
+                zeroPad = true;
+                break;
+            case '#':
+                prefixBaseX = true;
+                break;
+            }
+        }
+
+        // parameters may be null, undefined, empty-string or real valued
+        // we want to ignore null, undefined and empty-string values
+        if (!minWidth) {
+            minWidth = 0;
+        } else if (minWidth == '*') {
+            minWidth = +a[i++];
+        } else if (minWidth.charAt(0) == '*') {
+            minWidth = +a[minWidth.slice(1, -1)];
+        } else {
+            minWidth = +minWidth;
+        }
+
+        // Note: undocumented perl feature:
+        if (minWidth < 0) {
+            minWidth = -minWidth;
+            leftJustify = true;
+        }
+
+        if (!isFinite(minWidth)) {
+            throw new Error('sprintf: (minimum-)width must be finite');
+        }
+
+        if (!precision) {
+            precision = 'fFeE'.indexOf(type) > -1 ? 6 : (type == 'd') ? 0 : undefined;
+        } else if (precision == '*') {
+            precision = +a[i++];
+        } else if (precision.charAt(0) == '*') {
+            precision = +a[precision.slice(1, -1)];
+        } else {
+            precision = +precision;
+        }
+
+        // grab value using valueIndex if required?
+        value = valueIndex ? a[valueIndex.slice(0, -1)] : a[i++];
+
+        switch (type) {
+        case 's':
+            return formatString(String(value), leftJustify, minWidth, precision, zeroPad, customPadChar);
+        case 'c':
+            return formatString(String.fromCharCode(+value), leftJustify, minWidth, precision, zeroPad);
+        case 'b':
+            return formatBaseX(value, 2, prefixBaseX, leftJustify, minWidth, precision, zeroPad);
+        case 'o':
+            return formatBaseX(value, 8, prefixBaseX, leftJustify, minWidth, precision, zeroPad);
+        case 'x':
+            return formatBaseX(value, 16, prefixBaseX, leftJustify, minWidth, precision, zeroPad);
+        case 'X':
+            return formatBaseX(value, 16, prefixBaseX, leftJustify, minWidth, precision, zeroPad).toUpperCase();
+        case 'u':
+            return formatBaseX(value, 10, prefixBaseX, leftJustify, minWidth, precision, zeroPad);
+        case 'i':
+        case 'd':
+            number = (+value) | 0;
+            prefix = number < 0 ? '-' : positivePrefix;
+            value = prefix + pad(String(Math.abs(number)), precision, '0', false);
+            return justify(value, prefix, leftJustify, minWidth, zeroPad);
+        case 'e':
+        case 'E':
+        case 'f':
+        case 'F':
+        case 'g':
+        case 'G':
+            number = +value;
+            prefix = number < 0 ? '-' : positivePrefix;
+            method = ['toExponential', 'toFixed', 'toPrecision']['efg'.indexOf(type.toLowerCase())];
+            textTransform = ['toString', 'toUpperCase']['eEfFgG'.indexOf(type) % 2];
+            value = prefix + Math.abs(number)[method](precision);
+            return justify(value, prefix, leftJustify, minWidth, zeroPad)[textTransform]();
+        default:
+            return substring;
+        }
+    };
+
+    return format.replace(regex, doFormat);
+}
diff -ruNp ejudge-2.3.18/super_html_2.c ejudge/super_html_2.c
--- ejudge-2.3.18/super_html_2.c	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/super_html_2.c	2011-07-11 09:02:04.561273069 +0400
@@ -1,5 +1,5 @@
 /* -*- mode: c -*- */
-/* $Id: super_html_2.c 6162 2011-03-27 07:07:27Z cher $ */
+/* $Id: super_html_2.c 6351 2011-05-24 19:28:45Z cher $ */
 
 /* Copyright (C) 2005-2011 Alexander Chernov <cher@ejudge.ru> */
 
@@ -125,6 +125,8 @@ super_html_clear_variable(struct sid_sta
   case SSERV_CMD_CNTS_CLEAR_TEAM_URL: p_str = &cnts->team_url; break;
   case SSERV_CMD_CNTS_CLEAR_STANDINGS_URL: p_str = &cnts->standings_url; break;
   case SSERV_CMD_CNTS_CLEAR_PROBLEMS_URL: p_str = &cnts->problems_url; break;
+  case SSERV_CMD_CNTS_CLEAR_LOGO_URL: p_str = &cnts->logo_url; break;
+  case SSERV_CMD_CNTS_CLEAR_CSS_URL: p_str = &cnts->css_url; break;
   case SSERV_CMD_CNTS_CLEAR_ROOT_DIR: p_str = &cnts->root_dir; break;
   case SSERV_CMD_CNTS_CLEAR_CONF_DIR: p_str = &cnts->conf_dir; break;
   case SSERV_CMD_CNTS_CLEAR_DIR_MODE: p_str = &cnts->dir_mode; break;
@@ -265,6 +267,7 @@ super_html_set_contest_var(struct sid_st
                            int param3, int param4, int param5)
 {
   unsigned char **p_str = 0, **p_str_d2u = 0;
+  unsigned char **p_email = 0;
   unsigned char *p_bool = 0;
   time_t *p_date = 0;
   int v, n, memb_ind;
@@ -450,13 +453,13 @@ super_html_set_contest_var(struct sid_st
     p_str = &cnts->allowed_regions;
     break;
   case SSERV_CMD_CNTS_CHANGE_CF_NOTIFY_EMAIL:
-    p_str = &cnts->cf_notify_email;
+    p_email = &cnts->cf_notify_email;
     break;
   case SSERV_CMD_CNTS_CHANGE_CLAR_NOTIFY_EMAIL:
-    p_str = &cnts->clar_notify_email;
+    p_email = &cnts->clar_notify_email;
     break;
   case SSERV_CMD_CNTS_CHANGE_DAILY_STAT_EMAIL:
-    p_str = &cnts->daily_stat_email;
+    p_email = &cnts->daily_stat_email;
     break;
   case SSERV_CMD_CNTS_CHANGE_TEAM_HEAD_STYLE:
     p_str = &cnts->team_head_style;
@@ -465,7 +468,7 @@ super_html_set_contest_var(struct sid_st
     p_str = &cnts->team_par_style;
     break;
   case SSERV_CMD_CNTS_CHANGE_REGISTER_EMAIL:
-    p_str = &cnts->register_email;
+    p_email = &cnts->register_email;
     break;
   case SSERV_CMD_CNTS_CHANGE_REGISTER_URL:
     p_str = &cnts->register_url;
@@ -488,6 +491,12 @@ super_html_set_contest_var(struct sid_st
   case SSERV_CMD_CNTS_CHANGE_PROBLEMS_URL:
     p_str = &cnts->problems_url;
     break;
+  case SSERV_CMD_CNTS_CHANGE_LOGO_URL:
+    p_str = &cnts->logo_url;
+    break;
+  case SSERV_CMD_CNTS_CHANGE_CSS_URL:
+    p_str = &cnts->css_url;
+    break;
   case SSERV_CMD_CNTS_CHANGE_ROOT_DIR:
     p_str = &cnts->root_dir;
     break;
@@ -716,6 +725,12 @@ super_html_set_contest_var(struct sid_st
     abort();
   }
 
+  if (p_email) {
+    if (param2 && *param2 && !is_valid_email_address(param2))
+      return -SSERV_ERR_INVALID_PARAMETER;
+    p_str = p_email; p_email = 0;
+  }
+
   if (p_date) {
     if (xml_parse_date("", 0, 0, param2, p_date) < 0)
       return -SSERV_ERR_INVALID_PARAMETER;
diff -ruNp ejudge-2.3.18/super_html_3.c ejudge/super_html_3.c
--- ejudge-2.3.18/super_html_3.c	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/super_html_3.c	2011-07-11 09:02:04.652271396 +0400
@@ -1,5 +1,5 @@
 /* -*- mode: c -*- */
-/* $Id: super_html_3.c 6240 2011-04-09 04:50:34Z cher $ */
+/* $Id: super_html_3.c 6351 2011-05-24 19:28:45Z cher $ */
 
 /* Copyright (C) 2005-2011 Alexander Chernov <cher@ejudge.ru> */
 
@@ -73,9 +73,6 @@ static const unsigned char * const form_
   " bgcolor=\"#e0e0e0\"",
 };
 
-static int
-num_suffix(const unsigned char *str);
-
 static void
 html_submit_button(FILE *f,
                    int action,
@@ -251,6 +248,7 @@ static const unsigned char * const actio
   [SSERV_CMD_CNTS_CHANGE_TEAM_URL] = "Contest.xml:team_url",
   [SSERV_CMD_CNTS_CHANGE_STANDINGS_URL] = "Contest.xml:standings_url",
   [SSERV_CMD_CNTS_CHANGE_PROBLEMS_URL] = "Contest.xml:problems_url",
+  [SSERV_CMD_CNTS_CHANGE_LOGO_URL] = "Contest.xml:logo_url",
   [SSERV_CMD_CNTS_CHANGE_ROOT_DIR] = "Contest.xml:root_dir",
   [SSERV_CMD_CNTS_CHANGE_CONF_DIR] = "Contest.xml:conf_dir",
   [SSERV_CMD_CNTS_CHANGE_DIR_MODE] = "Contest.xml:dir_mode",
@@ -2768,7 +2766,7 @@ super_html_global_param(struct sid_state
                         int param3, int param4)
 {
   struct section_global_data *global = sstate->global;
-  int hh, mm, n, val, default_val, mult;
+  int hh, mm, n, val, default_val;
   unsigned char *s;
   int *p_int;
   unsigned char *p_str;
@@ -3097,16 +3095,8 @@ super_html_global_param(struct sid_state
     p_size = &global->compile_max_vm_size;
 
   handle_size_t:
-    if (!param2) return -SSERV_ERR_INVALID_PARAMETER;
-    if (sscanf(param2, "%d%n", &val, &n) == 1 && !param2[n] && val == -1) {
-      *p_size = -1L;
-      return 0;
-    }
-    if (sscanf(param2, "%zu%n", &zval, &n) != 1)
-      return -SSERV_ERR_INVALID_PARAMETER;
-    if (!(mult = num_suffix(param2 + n))) return -SSERV_ERR_INVALID_PARAMETER;
-    // FIXME: check for overflow
-    zval *= mult;
+    zval = 0;
+    if (size_str_to_size_t(param2, &zval) < 0) return -SSERV_ERR_INVALID_PARAMETER;
     *p_size = zval;
     return 0;
 
@@ -4470,7 +4460,7 @@ super_html_lang_cmd(struct sid_state *ss
                     int param3, int param4)
 {
   struct section_language_data *pl_old, *pl_new;
-  int val, n, mult;
+  int val, n;
   int *p_int;
   size_t *p_size, zval;
   char **tmp_env = 0;
@@ -4587,16 +4577,8 @@ super_html_lang_cmd(struct sid_state *ss
     p_size = &pl_new->max_vm_size;
 
   handle_size_t:
-    if (!param2) return -SSERV_ERR_INVALID_PARAMETER;
-    if (sscanf(param2, "%d%n", &val, &n) == 1 && !param2[n] && val == -1) {
-      *p_size = -1L;
-      return 0;
-    }
-    if (sscanf(param2, "%zu%n", &zval, &n) != 1)
-      return -SSERV_ERR_INVALID_PARAMETER;
-    if (!(mult = num_suffix(param2 + n))) return -SSERV_ERR_INVALID_PARAMETER;
-    // FIXME: check for overflow
-    zval *= mult;
+    zval = 0;
+    if (size_str_to_size_t(param2, &zval) < 0) return -SSERV_ERR_INVALID_PARAMETER;
     *p_size = zval;
     return 0;
 
@@ -7373,24 +7355,13 @@ super_html_prob_cmd(struct sid_state *ss
 #define PROB_ASSIGN_STRING(f) snprintf(prob->f, sizeof(prob->f), "%s", param2)
 #define PROB_CLEAR_STRING(f) prob->f[0] = 0
 
-static int
-num_suffix(const unsigned char *str)
-{
-  if (!str[0]) return 1;
-  if (str[1]) return 0; 
-  if (str[0] == 'k' || str[0] == 'K') return 1024;
-  if (str[0] == 'm' || str[0] == 'M') return 1024 * 1024;
-  if (str[0] == 'g' || str[0] == 'G') return 1024 * 1024 * 1024;
-  return 0;
-}
-
 int
 super_html_prob_param(struct sid_state *sstate, int cmd,
                       int prob_id, const unsigned char *param2,
                       int param3, int param4)
 {
   struct section_problem_data *prob;
-  int i, n, val, mult;
+  int i, n, val;
   int *p_int;
   char **tmp_env = 0;
   size_t *p_size, zval;
@@ -7758,16 +7729,8 @@ super_html_prob_param(struct sid_state *
     p_size = &prob->max_vm_size;
 
   handle_size_t:
-    if (!param2) return -SSERV_ERR_INVALID_PARAMETER;
-    if (sscanf(param2, "%d%n", &val, &n) == 1 && !param2[n] && val == -1) {
-      *p_size = -1L;
-      return 0;
-    }
-    if (sscanf(param2, "%zu%n", &zval, &n) != 1)
-      return -SSERV_ERR_INVALID_PARAMETER;
-    if (!(mult = num_suffix(param2 + n))) return -SSERV_ERR_INVALID_PARAMETER;
-    // FIXME: check for overflow
-    zval *= mult;
+    zval = 0;
+    if (size_str_to_size_t(param2, &zval) < 0) return -SSERV_ERR_INVALID_PARAMETER;
     *p_size = zval;
     return 0;
 
diff -ruNp ejudge-2.3.18/super_html_4.c ejudge/super_html_4.c
--- ejudge-2.3.18/super_html_4.c	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/super_html_4.c	2011-07-11 09:02:04.730269961 +0400
@@ -1,5 +1,5 @@
 /* -*- mode: c -*- */
-/* $Id: super_html_4.c 6235 2011-04-08 17:43:53Z cher $ */
+/* $Id: super_html_4.c 6401 2011-07-09 09:38:23Z cher $ */
 
 /* Copyright (C) 2008-2011 Alexander Chernov <cher@ejudge.ru> */
 
@@ -40,6 +40,7 @@
 #include "prepare_dflt.h"
 #include "cpu.h"
 #include "compat.h"
+#include "errlog.h"
 
 #include "reuse_xalloc.h"
 #include "reuse_logger.h"
@@ -50,6 +51,7 @@
 #include <stdlib.h>
 #include <stdarg.h>
 #include <sys/stat.h>
+#include <dlfcn.h>
 
 #if !defined CONF_STYLE_PREFIX
 #define CONF_STYLE_PREFIX "/ejudge/"
@@ -180,7 +182,7 @@ ss_cgi_nname(
   return 0;
 }
 
-static int
+int
 ss_cgi_param_int(
         struct super_http_request_info *phr,
         const unsigned char *name,
@@ -198,7 +200,7 @@ ss_cgi_param_int(
   return 0;
 }
 
-static int
+int
 ss_cgi_param_int_opt(
         struct super_http_request_info *phr,
         const unsigned char *name,
@@ -407,8 +409,8 @@ write_html_header(
             "</style>\n",
             CONF_STYLE_PREFIX, CONF_STYLE_PREFIX, CONF_STYLE_PREFIX,
             CONF_STYLE_PREFIX, CONF_STYLE_PREFIX);
+    fprintf(out_f, "<style type=\"text/css\" id=\"generatedStyles\"></style>\n");
   }
-  fprintf(out_f, "<style type=\"text/css\" id=\"generatedStyles\"></style>\n");
 
   if (use_dojo) {
     fprintf(out_f, "<script type=\"text/javascript\" src=\"%sdojo/dojo.js\" djConfig=\"isDebug: false, parseOnLoad: true, dojoIframeHistoryUrl:'%sdojo/resources/iframe_history.html'\"></script>\n",
@@ -992,6 +994,8 @@ static const struct cnts_edit_info cnts_
   { NS_CONTEST, CNTS_team_url, 's', 1, 1, 1, 1, 0, "URL for the client CGI program", "URL for the client CGI program", 0 },
   { NS_CONTEST, CNTS_standings_url, 's', 1, 1, 1, 1, 0, "URL for the current standings", "URL for the current standings", 0 },
   { NS_CONTEST, CNTS_problems_url, 's', 1, 1, 1, 1, 0, "URL for the problemset", "URL for the problemset", 0 },
+  { NS_CONTEST, CNTS_logo_url, 's', 1, 1, 1, 1, 0, "URL for the contest logo", "URL for the contest logo", 0 },
+  { NS_CONTEST, CNTS_css_url, 's', 1, 1, 1, 1, 0, "URL for the contest CSS", "URL for the contest CSS", 0 },
   { 0, 0, '-', 0, 0, 0, 0, 0, "Contest Management", 0, 0 },
   { NS_CONTEST, CNTS_managed, 'y', 1, 0, 0, 0, 0, "Enable the contest service", "Enable the contest service", 0 },
   { NS_CONTEST, CNTS_run_managed, 'y', 1, 0, 0, 0, 0, "Enable the run service", "Enable the run service", 0 },
@@ -2977,6 +2981,14 @@ cmd_edit_contest_xml_field(
         if ((newval = l10n_parse_locale(valstr)) < 0)
           FAIL(S_ERR_INV_VALUE);
       }
+      if (f_id == CNTS_register_email
+          || f_id == CNTS_cf_notify_email
+          || f_id == CNTS_clar_notify_email
+          || f_id == CNTS_daily_stat_email) {
+        if (valstr && *valstr && !is_valid_email_address(valstr)) {
+          FAIL(S_ERR_INV_VALUE);
+        }
+      }
 
       if (!*p_str) {
         retval = 1;
@@ -6476,6 +6488,203 @@ cmd_op_edit_serve_prob_field_detail(
   return retval;
 }
 
+static const unsigned char * const op_names[SSERV_OP_LAST] =
+{
+  [SSERV_OP_VIEW_CNTS_DETAILS] = "VIEW_CNTS_DETAILS",
+  [SSERV_OP_EDITED_CNTS_BACK] = "EDITED_CNTS_BACK",
+  [SSERV_OP_EDITED_CNTS_CONTINUE] = "EDITED_CNTS_CONTINUE",
+  [SSERV_OP_EDITED_CNTS_START_NEW] = "EDITED_CNTS_START_NEW",
+  [SSERV_OP_LOCKED_CNTS_FORGET] = "LOCKED_CNTS_FORGET",
+  [SSERV_OP_LOCKED_CNTS_CONTINUE] = "LOCKED_CNTS_CONTINUE",
+  [SSERV_OP_EDIT_CONTEST_PAGE] = "EDIT_CONTEST_PAGE",
+  [SSERV_OP_EDIT_CONTEST_PAGE_2] = "EDIT_CONTEST_PAGE_2",
+  [SSERV_OP_CLEAR_CONTEST_XML_FIELD] = "CLEAR_CONTEST_XML_FIELD",
+  [SSERV_OP_EDIT_CONTEST_XML_FIELD] = "EDIT_CONTEST_XML_FIELD",
+  [SSERV_OP_TOGGLE_CONTEST_XML_VISIBILITY] = "TOGGLE_CONTEST_XML_VISIBILITY",
+  [SSERV_OP_CONTEST_XML_FIELD_EDIT_PAGE] = "CONTEST_XML_FIELD_EDIT_PAGE",
+  [SSERV_OP_SAVE_FILE_CONTEST_XML] = "SAVE_FILE_CONTEST_XML",
+  [SSERV_OP_CLEAR_FILE_CONTEST_XML] = "CLEAR_FILE_CONTEST_XML",
+  [SSERV_OP_RELOAD_FILE_CONTEST_XML] = "RELOAD_FILE_CONTEST_XML",
+  [SSERV_OP_COPY_ACCESS_RULES_PAGE] = "COPY_ACCESS_RULES_PAGE",
+  [SSERV_OP_COPY_ALL_ACCESS_RULES_PAGE] = "COPY_ALL_ACCESS_RULES_PAGE",
+  [SSERV_OP_COPY_ALL_ACCESS_RULES] = "COPY_ALL_ACCESS_RULES",
+  [SSERV_OP_COPY_ALL_PRIV_USERS_PAGE] = "COPY_ALL_PRIV_USERS_PAGE",
+  [SSERV_OP_COPY_ALL_PRIV_USERS] = "COPY_ALL_PRIV_USERS",
+  [SSERV_OP_ADD_PRIV_USER] = "ADD_PRIV_USER",
+  [SSERV_OP_EDIT_PERMISSIONS_PAGE] = "EDIT_PERMISSIONS_PAGE",
+  [SSERV_OP_EDIT_GENERAL_FIELDS_PAGE] = "EDIT_GENERAL_FIELDS_PAGE",
+  [SSERV_OP_EDIT_MEMBER_FIELDS_PAGE] = "EDIT_MEMBER_FIELDS_PAGE",
+  [SSERV_OP_DELETE_PRIV_USER] = "DELETE_PRIV_USER",
+  [SSERV_OP_SET_PREDEF_PRIV] = "SET_PREDEF_PRIV",
+  [SSERV_OP_SET_PRIV] = "SET_PRIV",
+  [SSERV_OP_SET_DEFAULT_ACCESS] = "SET_DEFAULT_ACCESS",
+  [SSERV_OP_CHECK_IP_MASK] = "CHECK_IP_MASK",
+  [SSERV_OP_ADD_IP] = "ADD_IP",
+  [SSERV_OP_SET_RULE_ACCESS] = "SET_RULE_ACCESS",
+  [SSERV_OP_SET_RULE_SSL] = "SET_RULE_SSL",
+  [SSERV_OP_SET_RULE_IP] = "SET_RULE_IP",
+  [SSERV_OP_DELETE_RULE] = "DELETE_RULE",
+  [SSERV_OP_FORWARD_RULE] = "FORWARD_RULE",
+  [SSERV_OP_BACKWARD_RULE] = "BACKWARD_RULE",
+  [SSERV_OP_COPY_ACCESS_RULES] = "COPY_ACCESS_RULES",
+  [SSERV_OP_EDIT_GENERAL_FIELDS] = "EDIT_GENERAL_FIELDS",
+  [SSERV_OP_EDIT_MEMBER_FIELDS] = "EDIT_MEMBER_FIELDS",
+  [SSERV_OP_CREATE_NEW_CONTEST_PAGE] = "CREATE_NEW_CONTEST_PAGE",
+  [SSERV_OP_CREATE_NEW_CONTEST] = "CREATE_NEW_CONTEST",
+  [SSERV_OP_FORGET_CONTEST] = "FORGET_CONTEST",
+  [SSERV_OP_EDIT_SERVE_GLOBAL_FIELD] = "EDIT_SERVE_GLOBAL_FIELD",
+  [SSERV_OP_CLEAR_SERVE_GLOBAL_FIELD] = "CLEAR_SERVE_GLOBAL_FIELD",
+  [SSERV_OP_EDIT_SID_STATE_FIELD] = "EDIT_SID_STATE_FIELD",
+  [SSERV_OP_EDIT_SID_STATE_FIELD_NEGATED] = "EDIT_SID_STATE_FIELD_NEGATED",
+  [SSERV_OP_EDIT_SERVE_GLOBAL_FIELD_DETAIL_PAGE] = "EDIT_SERVE_GLOBAL_FIELD_DETAIL_PAGE",
+  [SSERV_OP_EDIT_SERVE_GLOBAL_FIELD_DETAIL] = "EDIT_SERVE_GLOBAL_FIELD_DETAIL",
+  [SSERV_OP_SET_SID_STATE_LANG_FIELD] = "SET_SID_STATE_LANG_FIELD",
+  [SSERV_OP_CLEAR_SID_STATE_LANG_FIELD] = "CLEAR_SID_STATE_LANG_FIELD",
+  [SSERV_OP_SET_SERVE_LANG_FIELD] = "SET_SERVE_LANG_FIELD",
+  [SSERV_OP_CLEAR_SERVE_LANG_FIELD] = "CLEAR_SERVE_LANG_FIELD",
+  [SSERV_OP_EDIT_SERVE_LANG_FIELD_DETAIL_PAGE] = "EDIT_SERVE_LANG_FIELD_DETAIL_PAGE",
+  [SSERV_OP_EDIT_SERVE_LANG_FIELD_DETAIL] = "EDIT_SERVE_LANG_FIELD_DETAIL",
+  [SSERV_OP_SERVE_LANG_UPDATE_VERSIONS] = "SERVE_LANG_UPDATE_VERSIONS",
+  [SSERV_OP_CREATE_ABSTR_PROB] = "CREATE_ABSTR_PROB",
+  [SSERV_OP_CREATE_CONCRETE_PROB] = "CREATE_CONCRETE_PROB",
+  [SSERV_OP_DELETE_PROB] = "DELETE_PROB",
+  [SSERV_OP_SET_SID_STATE_PROB_FIELD] = "SET_SID_STATE_PROB_FIELD",
+  [SSERV_OP_SET_SERVE_PROB_FIELD] = "SET_SERVE_PROB_FIELD",
+  [SSERV_OP_CLEAR_SERVE_PROB_FIELD] = "CLEAR_SERVE_PROB_FIELD",
+  [SSERV_OP_EDIT_SERVE_PROB_FIELD_DETAIL_PAGE] = "EDIT_SERVE_PROB_FIELD_DETAIL_PAGE",
+  [SSERV_OP_EDIT_SERVE_PROB_FIELD_DETAIL] = "EDIT_SERVE_PROB_FIELD_DETAIL",
+  [SSERV_OP_BROWSE_PROBLEM_PACKAGES] = "BROWSE_PROBLEM_PACKAGES",
+  [SSERV_OP_CREATE_PACKAGE] = "CREATE_PACKAGE",
+  [SSERV_OP_CREATE_PROBLEM] = "CREATE_PROBLEM",
+  [SSERV_OP_DELETE_ITEM] = "DELETE_ITEM",
+  [SSERV_OP_EDIT_PROBLEM] = "EDIT_PROBLEM",
+  [SSERV_OP_USER_BROWSE_PAGE] = "USER_BROWSE_PAGE",
+  [SSERV_OP_USER_FILTER_CHANGE_ACTION] = "USER_FILTER_CHANGE_ACTION",
+  [SSERV_OP_USER_FILTER_FIRST_PAGE_ACTION] = "USER_FILTER_FIRST_PAGE_ACTION",
+  [SSERV_OP_USER_FILTER_PREV_PAGE_ACTION] = "USER_FILTER_PREV_PAGE_ACTION",
+  [SSERV_OP_USER_FILTER_NEXT_PAGE_ACTION] = "USER_FILTER_NEXT_PAGE_ACTION",
+  [SSERV_OP_USER_FILTER_LAST_PAGE_ACTION] = "USER_FILTER_LAST_PAGE_ACTION",
+  [SSERV_OP_USER_JUMP_CONTEST_ACTION] = "USER_JUMP_CONTEST_ACTION",
+  [SSERV_OP_USER_JUMP_GROUP_ACTION] = "USER_JUMP_GROUP_ACTION",
+  [SSERV_OP_USER_BROWSE_MARK_ALL_ACTION] = "USER_BROWSE_MARK_ALL_ACTION",
+  [SSERV_OP_USER_BROWSE_UNMARK_ALL_ACTION] = "USER_BROWSE_UNMARK_ALL_ACTION",
+  [SSERV_OP_USER_BROWSE_TOGGLE_ALL_ACTION] = "USER_BROWSE_TOGGLE_ALL_ACTION",
+  [SSERV_OP_USER_CREATE_ONE_PAGE] = "USER_CREATE_ONE_PAGE",
+  [SSERV_OP_USER_CREATE_ONE_ACTION] = "USER_CREATE_ONE_ACTION",
+  [SSERV_OP_USER_CREATE_MANY_PAGE] = "USER_CREATE_MANY_PAGE",
+  [SSERV_OP_USER_CREATE_MANY_ACTION] = "USER_CREATE_MANY_ACTION",
+  [SSERV_OP_USER_CREATE_FROM_CSV_PAGE] = "USER_CREATE_FROM_CSV_PAGE",
+  [SSERV_OP_USER_CREATE_FROM_CSV_ACTION] = "USER_CREATE_FROM_CSV_ACTION",
+  [SSERV_OP_USER_DETAIL_PAGE] = "USER_DETAIL_PAGE",
+  [SSERV_OP_USER_PASSWORD_PAGE] = "USER_PASSWORD_PAGE",
+  [SSERV_OP_USER_CHANGE_PASSWORD_ACTION] = "USER_CHANGE_PASSWORD_ACTION",
+  [SSERV_OP_USER_CNTS_PASSWORD_PAGE] = "USER_CNTS_PASSWORD_PAGE",
+  [SSERV_OP_USER_CHANGE_CNTS_PASSWORD_ACTION] = "USER_CHANGE_CNTS_PASSWORD_ACTION",
+  [SSERV_OP_USER_CLEAR_FIELD_ACTION] = "USER_CLEAR_FIELD_ACTION",
+  [SSERV_OP_USER_CREATE_MEMBER_ACTION] = "USER_CREATE_MEMBER_ACTION",
+  [SSERV_OP_USER_DELETE_MEMBER_PAGE] = "USER_DELETE_MEMBER_PAGE",
+  [SSERV_OP_USER_DELETE_MEMBER_ACTION] = "USER_DELETE_MEMBER_ACTION",
+  [SSERV_OP_USER_SAVE_AND_PREV_ACTION] = "USER_SAVE_AND_PREV_ACTION",
+  [SSERV_OP_USER_SAVE_ACTION] = "USER_SAVE_ACTION",
+  [SSERV_OP_USER_SAVE_AND_NEXT_ACTION] = "USER_SAVE_AND_NEXT_ACTION",
+  [SSERV_OP_USER_CANCEL_AND_PREV_ACTION] = "USER_CANCEL_AND_PREV_ACTION",
+  [SSERV_OP_USER_CANCEL_ACTION] = "USER_CANCEL_ACTION",
+  [SSERV_OP_USER_CANCEL_AND_NEXT_ACTION] = "USER_CANCEL_AND_NEXT_ACTION",
+  [SSERV_OP_USER_CREATE_REG_PAGE] = "USER_CREATE_REG_PAGE",
+  [SSERV_OP_USER_CREATE_REG_ACTION] = "USER_CREATE_REG_ACTION",
+  [SSERV_OP_USER_EDIT_REG_PAGE] = "USER_EDIT_REG_PAGE",
+  [SSERV_OP_USER_EDIT_REG_ACTION] = "USER_EDIT_REG_ACTION",
+  [SSERV_OP_USER_DELETE_REG_PAGE] = "USER_DELETE_REG_PAGE",
+  [SSERV_OP_USER_DELETE_REG_ACTION] = "USER_DELETE_REG_ACTION",
+  [SSERV_OP_USER_DELETE_SESSION_ACTION] = "USER_DELETE_SESSION_ACTION",
+  [SSERV_OP_USER_DELETE_ALL_SESSIONS_ACTION] = "USER_DELETE_ALL_SESSIONS_ACTION",
+  [SSERV_OP_USER_SEL_RANDOM_PASSWD_PAGE] = "USER_SEL_RANDOM_PASSWD_PAGE",
+  [SSERV_OP_USER_SEL_RANDOM_PASSWD_ACTION] = "USER_SEL_RANDOM_PASSWD_ACTION",
+  [SSERV_OP_USER_SEL_CLEAR_CNTS_PASSWD_PAGE] = "USER_SEL_CLEAR_CNTS_PASSWD_PAGE",
+  [SSERV_OP_USER_SEL_CLEAR_CNTS_PASSWD_ACTION] = "USER_SEL_CLEAR_CNTS_PASSWD_ACTION",
+  [SSERV_OP_USER_SEL_RANDOM_CNTS_PASSWD_PAGE] = "USER_SEL_RANDOM_CNTS_PASSWD_PAGE",
+  [SSERV_OP_USER_SEL_RANDOM_CNTS_PASSWD_ACTION] = "USER_SEL_RANDOM_CNTS_PASSWD_ACTION",
+  [SSERV_OP_USER_SEL_CREATE_REG_PAGE] = "USER_SEL_CREATE_REG_PAGE",
+  [SSERV_OP_USER_SEL_CREATE_REG_ACTION] = "USER_SEL_CREATE_REG_ACTION",
+  [SSERV_OP_USER_SEL_CREATE_REG_AND_COPY_PAGE] = "USER_SEL_CREATE_REG_AND_COPY_PAGE",
+  [SSERV_OP_USER_SEL_CREATE_REG_AND_COPY_ACTION] = "USER_SEL_CREATE_REG_AND_COPY_ACTION",
+  [SSERV_OP_USER_SEL_DELETE_REG_PAGE] = "USER_SEL_DELETE_REG_PAGE",
+  [SSERV_OP_USER_SEL_DELETE_REG_ACTION] = "USER_SEL_DELETE_REG_ACTION",
+  [SSERV_OP_USER_SEL_CHANGE_REG_STATUS_PAGE] = "USER_SEL_CHANGE_REG_STATUS_PAGE",
+  [SSERV_OP_USER_SEL_CHANGE_REG_STATUS_ACTION] = "USER_SEL_CHANGE_REG_STATUS_ACTION",
+  [SSERV_OP_USER_SEL_CHANGE_REG_FLAGS_PAGE] = "USER_SEL_CHANGE_REG_FLAGS_PAGE",
+  [SSERV_OP_USER_SEL_CHANGE_REG_FLAGS_ACTION] = "USER_SEL_CHANGE_REG_FLAGS_ACTION",
+  [SSERV_OP_USER_SEL_CANCEL_ACTION] = "USER_SEL_CANCEL_ACTION",
+  [SSERV_OP_USER_SEL_VIEW_PASSWD_PAGE] = "USER_SEL_VIEW_PASSWD_PAGE",
+  [SSERV_OP_USER_SEL_VIEW_CNTS_PASSWD_PAGE] = "USER_SEL_VIEW_CNTS_PASSWD_PAGE",
+  [SSERV_OP_USER_SEL_VIEW_PASSWD_REDIRECT] = "USER_SEL_VIEW_PASSWD_REDIRECT",
+  [SSERV_OP_USER_SEL_VIEW_CNTS_PASSWD_REDIRECT] = "USER_SEL_VIEW_CNTS_PASSWD_REDIRECT",
+  [SSERV_OP_USER_SEL_CREATE_GROUP_MEMBER_PAGE] = "USER_SEL_CREATE_GROUP_MEMBER_PAGE",
+  [SSERV_OP_USER_SEL_CREATE_GROUP_MEMBER_ACTION] = "USER_SEL_CREATE_GROUP_MEMBER_ACTION",
+  [SSERV_OP_USER_SEL_DELETE_GROUP_MEMBER_PAGE] = "USER_SEL_DELETE_GROUP_MEMBER_PAGE",
+  [SSERV_OP_USER_SEL_DELETE_GROUP_MEMBER_ACTION] = "USER_SEL_DELETE_GROUP_MEMBER_ACTION",
+  [SSERV_OP_GROUP_BROWSE_PAGE] = "GROUP_BROWSE_PAGE",
+  [SSERV_OP_GROUP_FILTER_CHANGE_ACTION] = "GROUP_FILTER_CHANGE_ACTION",
+  [SSERV_OP_GROUP_FILTER_FIRST_PAGE_ACTION] = "GROUP_FILTER_FIRST_PAGE_ACTION",
+  [SSERV_OP_GROUP_FILTER_PREV_PAGE_ACTION] = "GROUP_FILTER_PREV_PAGE_ACTION",
+  [SSERV_OP_GROUP_FILTER_NEXT_PAGE_ACTION] = "GROUP_FILTER_NEXT_PAGE_ACTION",
+  [SSERV_OP_GROUP_FILTER_LAST_PAGE_ACTION] = "GROUP_FILTER_LAST_PAGE_ACTION",
+  [SSERV_OP_GROUP_CREATE_PAGE] = "GROUP_CREATE_PAGE",
+  [SSERV_OP_GROUP_CREATE_ACTION] = "GROUP_CREATE_ACTION",
+  [SSERV_OP_GROUP_MODIFY_PAGE] = "GROUP_MODIFY_PAGE",
+  [SSERV_OP_GROUP_MODIFY_PAGE_ACTION] = "GROUP_MODIFY_PAGE_ACTION",
+  [SSERV_OP_GROUP_MODIFY_ACTION] = "GROUP_MODIFY_ACTION",
+  [SSERV_OP_GROUP_DELETE_PAGE] = "GROUP_DELETE_PAGE",
+  [SSERV_OP_GROUP_DELETE_PAGE_ACTION] = "GROUP_DELETE_PAGE_ACTION",
+  [SSERV_OP_GROUP_DELETE_ACTION] = "GROUP_DELETE_ACTION",
+  [SSERV_OP_GROUP_CANCEL_ACTION] = "GROUP_CANCEL_ACTION",
+};
+
+static const int op_redirect[SSERV_OP_LAST] =
+{
+  [SSERV_OP_USER_FILTER_FIRST_PAGE_ACTION] = SSERV_OP_USER_FILTER_CHANGE_ACTION,
+  [SSERV_OP_USER_FILTER_PREV_PAGE_ACTION] = SSERV_OP_USER_FILTER_CHANGE_ACTION,
+  [SSERV_OP_USER_FILTER_NEXT_PAGE_ACTION] = SSERV_OP_USER_FILTER_CHANGE_ACTION,
+  [SSERV_OP_USER_FILTER_LAST_PAGE_ACTION] = SSERV_OP_USER_FILTER_CHANGE_ACTION,
+
+  [SSERV_OP_USER_BROWSE_UNMARK_ALL_ACTION] = SSERV_OP_USER_BROWSE_MARK_ALL_ACTION,
+  [SSERV_OP_USER_BROWSE_TOGGLE_ALL_ACTION] = SSERV_OP_USER_BROWSE_MARK_ALL_ACTION,
+
+  [SSERV_OP_USER_SAVE_AND_PREV_ACTION] = SSERV_OP_USER_SAVE_ACTION,
+  [SSERV_OP_USER_SAVE_AND_NEXT_ACTION] = SSERV_OP_USER_SAVE_ACTION,
+
+  [SSERV_OP_USER_CANCEL_AND_PREV_ACTION] = SSERV_OP_USER_CANCEL_ACTION,
+  [SSERV_OP_USER_CANCEL_AND_NEXT_ACTION] = SSERV_OP_USER_CANCEL_ACTION,
+
+  [SSERV_OP_USER_SEL_CLEAR_CNTS_PASSWD_PAGE] = SSERV_OP_USER_SEL_RANDOM_PASSWD_PAGE,
+  [SSERV_OP_USER_SEL_RANDOM_CNTS_PASSWD_PAGE] = SSERV_OP_USER_SEL_RANDOM_PASSWD_PAGE,
+  [SSERV_OP_USER_SEL_DELETE_REG_PAGE] = SSERV_OP_USER_SEL_RANDOM_PASSWD_PAGE,
+  [SSERV_OP_USER_SEL_CHANGE_REG_STATUS_PAGE] = SSERV_OP_USER_SEL_RANDOM_PASSWD_PAGE,
+  [SSERV_OP_USER_SEL_CHANGE_REG_FLAGS_PAGE] = SSERV_OP_USER_SEL_RANDOM_PASSWD_PAGE,
+  [SSERV_OP_USER_SEL_CREATE_REG_PAGE] = SSERV_OP_USER_SEL_RANDOM_PASSWD_PAGE,
+  [SSERV_OP_USER_SEL_CREATE_REG_AND_COPY_PAGE] = SSERV_OP_USER_SEL_RANDOM_PASSWD_PAGE,
+  [SSERV_OP_USER_SEL_CREATE_GROUP_MEMBER_PAGE] = SSERV_OP_USER_SEL_RANDOM_PASSWD_PAGE,
+  [SSERV_OP_USER_SEL_DELETE_GROUP_MEMBER_PAGE] = SSERV_OP_USER_SEL_RANDOM_PASSWD_PAGE,
+
+  [SSERV_OP_USER_SEL_CLEAR_CNTS_PASSWD_ACTION] = SSERV_OP_USER_SEL_RANDOM_PASSWD_ACTION,
+  [SSERV_OP_USER_SEL_RANDOM_CNTS_PASSWD_ACTION] = SSERV_OP_USER_SEL_RANDOM_PASSWD_ACTION,
+  [SSERV_OP_USER_SEL_DELETE_REG_ACTION] = SSERV_OP_USER_SEL_RANDOM_PASSWD_ACTION,
+  [SSERV_OP_USER_SEL_CHANGE_REG_STATUS_ACTION] = SSERV_OP_USER_SEL_RANDOM_PASSWD_ACTION,
+  [SSERV_OP_USER_SEL_CHANGE_REG_FLAGS_ACTION] = SSERV_OP_USER_SEL_RANDOM_PASSWD_ACTION,
+  [SSERV_OP_USER_SEL_CREATE_REG_ACTION] = SSERV_OP_USER_SEL_RANDOM_PASSWD_ACTION,
+  [SSERV_OP_USER_SEL_CREATE_REG_AND_COPY_ACTION] = SSERV_OP_USER_SEL_RANDOM_PASSWD_ACTION,
+  [SSERV_OP_USER_SEL_CREATE_GROUP_MEMBER_ACTION] = SSERV_OP_USER_SEL_RANDOM_PASSWD_ACTION,
+  [SSERV_OP_USER_SEL_DELETE_GROUP_MEMBER_ACTION] = SSERV_OP_USER_SEL_RANDOM_PASSWD_ACTION,
+
+  [SSERV_OP_USER_SEL_VIEW_CNTS_PASSWD_PAGE] = SSERV_OP_USER_SEL_VIEW_PASSWD_PAGE,
+  [SSERV_OP_USER_SEL_VIEW_CNTS_PASSWD_REDIRECT] = SSERV_OP_USER_SEL_VIEW_PASSWD_REDIRECT,
+
+  [SSERV_OP_GROUP_FILTER_FIRST_PAGE_ACTION] = SSERV_OP_GROUP_FILTER_CHANGE_ACTION,
+  [SSERV_OP_GROUP_FILTER_PREV_PAGE_ACTION] = SSERV_OP_GROUP_FILTER_CHANGE_ACTION,
+  [SSERV_OP_GROUP_FILTER_NEXT_PAGE_ACTION] = SSERV_OP_GROUP_FILTER_CHANGE_ACTION,
+  [SSERV_OP_GROUP_FILTER_LAST_PAGE_ACTION] = SSERV_OP_GROUP_FILTER_CHANGE_ACTION,
+};
+
 static handler_func_t op_handlers[SSERV_OP_LAST] =
 {
   [SSERV_OP_VIEW_CNTS_DETAILS] = cmd_cnts_details,
@@ -6541,24 +6750,79 @@ static handler_func_t op_handlers[SSERV_
   [SSERV_OP_CLEAR_SERVE_PROB_FIELD] = cmd_op_clear_serve_prob_field,
   [SSERV_OP_EDIT_SERVE_PROB_FIELD_DETAIL_PAGE] = cmd_op_edit_serve_prob_field_detail_page,
   [SSERV_OP_EDIT_SERVE_PROB_FIELD_DETAIL] = cmd_op_edit_serve_prob_field_detail,
+
   [SSERV_OP_BROWSE_PROBLEM_PACKAGES] = super_serve_op_browse_problem_packages,
   [SSERV_OP_CREATE_PACKAGE] = super_serve_op_package_operation,
   [SSERV_OP_CREATE_PROBLEM] = super_serve_op_edit_problem,
   [SSERV_OP_DELETE_ITEM] = super_serve_op_package_operation,
   [SSERV_OP_EDIT_PROBLEM] = super_serve_op_edit_problem,
+
+  /* Note: operations SSERV_OP_USER_*, SSERV_OP_GROUP_* are loaded using dlsym */
 };
 
+extern void super_html_6_force_link(void);
+void *super_html_6_force_link_ptr = super_html_6_force_link;
+
+static void *self_dl_handle = 0;
 static int
 do_http_request(FILE *log_f, FILE *out_f, struct super_http_request_info *phr)
 {
   int opcode = 0;
   int retval = 0;
+  int n = 0;
+  const unsigned char *s = 0;
 
-  if (ss_cgi_param_int(phr, "op", &opcode) < 0
-      || opcode <= 0 || opcode >= SSERV_OP_LAST || !op_handlers[opcode])
+  if ((s = ss_cgi_nname(phr, "op_", 3))) {
+    if (sscanf(s, "op_%d%n", &opcode, &n) != 1 || s[n]
+        || opcode <= 0 || opcode >= SSERV_OP_LAST)
+      FAIL(S_ERR_INV_OPER);
+  } else if (ss_cgi_param_int(phr, "op", &opcode) < 0
+      || opcode <= 0 || opcode >= SSERV_OP_LAST)
     FAIL(S_ERR_INV_OPER);
+
+  if (!op_names[opcode]) FAIL(S_ERR_INV_OPER);
+  if (op_handlers[opcode] == (handler_func_t) 1) FAIL(S_ERR_NOT_IMPLEMENTED);
   phr->opcode = opcode;
 
+  if (!op_handlers[opcode]) {
+    if (self_dl_handle == (void*) 1) FAIL(S_ERR_NOT_IMPLEMENTED);
+    self_dl_handle = dlopen(NULL, RTLD_NOW);
+    if (!self_dl_handle) {
+      err("do_http_request: dlopen failed: %s", dlerror());
+      self_dl_handle = (void*) 1;
+      FAIL(S_ERR_NOT_IMPLEMENTED);
+    }
+
+    int redir_opcode = opcode;
+    if (op_redirect[opcode] > 0) {
+      redir_opcode = op_redirect[opcode];
+      if (redir_opcode <= 0 || redir_opcode >= SSERV_OP_LAST || !op_names[redir_opcode]) {
+        err("do_http_request: invalid opcode redirect %d->%d", opcode, redir_opcode);
+        op_handlers[opcode] = (handler_func_t) 1;
+        FAIL(S_ERR_NOT_IMPLEMENTED);
+      }
+      if (op_handlers[redir_opcode] == (handler_func_t) 1) {
+        err("do_http_request: not implemented opcode redirect %d->%d", opcode, redir_opcode);
+        op_handlers[opcode] = (handler_func_t) 1;
+        FAIL(S_ERR_NOT_IMPLEMENTED);
+      }
+    }
+
+    if (op_handlers[redir_opcode]) {
+      op_handlers[opcode] = op_handlers[redir_opcode];
+    } else {
+      unsigned char func_name[512];
+      snprintf(func_name, sizeof(func_name), "super_serve_op_%s", op_names[redir_opcode]);
+      void *void_func = dlsym(self_dl_handle, func_name);
+      if (!void_func) {
+        err("do_http_request: function %s is not found", func_name);
+        op_handlers[opcode] = (handler_func_t) 1;
+        FAIL(S_ERR_NOT_IMPLEMENTED);
+      }
+      op_handlers[opcode] = (handler_func_t) void_func;
+    }
+  }
+
   retval = (*op_handlers[opcode])(log_f, out_f, phr);
 
  cleanup:
@@ -6586,6 +6850,34 @@ static unsigned char const * const error
   [S_ERR_INV_PACKAGE] = "Invalid package",
   [S_ERR_ITEM_EXISTS] = "Such item already exists",
   [S_ERR_OPERATION_FAILED] = "System operation failed",
+  [S_ERR_INV_USER_ID] = "Invalid User ID",
+  [S_ERR_NO_CONNECTION] = "No connection to the database",
+  [S_ERR_DB_ERROR] = "Database error",
+  [S_ERR_UNSPEC_PASSWD1] = "Password 1 is not specified",
+  [S_ERR_UNSPEC_PASSWD2] = "Password 2 is not specified",
+  [S_ERR_INV_PASSWD1] = "Password 1 is invalid",
+  [S_ERR_INV_PASSWD2] = "Password 2 is invalid",
+  [S_ERR_PASSWDS_DIFFER] = "Passwords do not match each other",
+  [S_ERR_UNSPEC_LOGIN] = "Login is not specified",
+  [S_ERR_DUPLICATED_LOGIN] = "This login is aready used",
+  [S_ERR_INV_GROUP_ID] = "Invalid group ID",
+  [S_ERR_INV_FIRST_SERIAL] = "Invalid first serial number",
+  [S_ERR_INV_LAST_SERIAL] = "Invalid last serial number",
+  [S_ERR_INV_RANGE] = "Invalid serial number range",
+  [S_ERR_INV_LOGIN_TEMPLATE] = "Invalid login template",
+  [S_ERR_INV_REG_PASSWORD_TEMPLATE] = "Invalid registration password template",
+  [S_ERR_INV_CNTS_PASSWORD_TEMPLATE] = "Invalid contest password template",
+  [S_ERR_INV_CNTS_NAME_TEMPLATE] = "Invalid name template",
+  [S_ERR_INV_CSV_FILE] = "Invalid CSV file",
+  [S_ERR_INV_CHARSET] = "Invalid charset",
+  [S_ERR_INV_SEPARATOR] = "Invalid field separator",
+  [S_ERR_DATA_READ_ONLY] = "Data is read-only",
+  [S_ERR_TOO_MANY_MEMBERS] = "Too many members",
+  [S_ERR_INV_SERIAL] = "Invalid member",
+  [S_ERR_INV_EMAIL] = "Invalid email",
+  [S_ERR_INV_GROUP_NAME] = "Invalid group name",
+  [S_ERR_INV_DESCRIPTION] = "Invalid description",
+  [S_ERR_GROUP_CREATION_FAILED] = "Group creation failed",
 };
 
 void
diff -ruNp ejudge-2.3.18/super_html_6.c ejudge/super_html_6.c
--- ejudge-2.3.18/super_html_6.c	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/super_html_6.c	2011-07-11 09:02:04.137280863 +0400
@@ -0,0 +1,7956 @@
+/* -*- mode: c -*- */
+/* $Id: super_html_6.c 6398 2011-07-07 05:06:12Z cher $ */
+
+/* Copyright (C) 2011 Alexander Chernov <cher@ejudge.ru> */
+
+/*
+ * 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.
+ */
+
+#include "config.h"
+#include "version.h"
+#include "ej_limits.h"
+
+#include "super_html.h"
+#include "super-serve.h"
+#include "super_proto.h"
+#include "mischtml.h"
+#include "userlist_proto.h"
+#include "userlist_clnt.h"
+#include "userlist.h"
+#include "misctext.h"
+#include "errlog.h"
+#include "xml_utils.h"
+#include "ejudge_cfg.h"
+#include "super_html_6.h"
+#include "super_html_6_meta.h"
+#include "meta_generic.h"
+#include "charsets.h"
+#include "csv.h"
+#include "bitset.h"
+
+#include "reuse_xalloc.h"
+
+#include <stdarg.h>
+#include <printf.h>
+#include <limits.h>
+
+#define ARMOR(s)  html_armor_buf(&ab, (s))
+#define FAIL(c) do { retval = -(c); goto cleanup; } while (0)
+
+#define FIRST_COOKIE(u) ((struct userlist_cookie*) (u)->cookies->first_down)
+#define NEXT_COOKIE(c)  ((struct userlist_cookie*) (c)->b.right)
+#define FIRST_CONTEST(u) ((struct userlist_contest*)(u)->contests->first_down)
+#define NEXT_CONTEST(c)  ((struct userlist_contest*)(c)->b.right)
+
+void
+super_html_6_force_link()
+{
+}
+
+unsigned char *
+ss_url_unescaped(
+        unsigned char *buf,
+        size_t size,
+        const struct super_http_request_info *phr,
+        int action,
+        int op,
+        const char *format,
+        ...)
+{
+  unsigned char fbuf[1024];
+  unsigned char abuf[64];
+  unsigned char obuf[64];
+  const unsigned char *sep = "";
+  va_list args;
+
+  fbuf[0] = 0;
+  if (format && *format) {
+    va_start(args, format);
+    vsnprintf(fbuf, sizeof(fbuf), format, args);
+    va_end(args);
+  }
+  if (fbuf[0]) sep = "&";
+
+  abuf[0] = 0;
+  if (action > 0) snprintf(abuf, sizeof(abuf), "&action=%d", action);
+  obuf[0] = 0;
+  if (op > 0) snprintf(obuf, sizeof(obuf), "&op=%d", op);
+
+  snprintf(buf, size, "%s?SID=%016llx%s%s%s%s", phr->self_url,
+           phr->session_id, abuf, obuf, sep, fbuf);
+  return buf;
+}
+
+void
+ss_redirect(
+        FILE *fout,
+        struct super_http_request_info *phr,
+        int new_op,
+        const unsigned char *extra)
+{
+  unsigned char url[1024];
+
+  if (extra && *extra) {
+    ss_url_unescaped(url, sizeof(url), phr, SSERV_CMD_HTTP_REQUEST, new_op, "%s", extra);
+  } else {
+    ss_url_unescaped(url, sizeof(url), phr, SSERV_CMD_HTTP_REQUEST, new_op, 0);
+  }
+
+  fprintf(fout, "Content-Type: text/html; charset=%s\nCache-Control: no-cache\nPragma: no-cache\nLocation: %s\n\n", EJUDGE_CHARSET, url);
+}
+
+void
+ss_redirect_2(
+        FILE *fout,
+        struct super_http_request_info *phr,
+        int new_op,
+        int contest_id,
+        int group_id,
+        int other_user_id,
+        const unsigned char *marked_str)
+{
+  unsigned char url[1024];
+  char *o_str = 0;
+  size_t o_len = 0;
+  FILE *o_out = 0;
+
+  o_out = open_memstream(&o_str, &o_len);
+  if (contest_id > 0) {
+    fprintf(o_out, "&contest_id=%d", contest_id);
+  }
+  if (group_id > 0) {
+    fprintf(o_out, "&group_id=%d", group_id);
+  }
+  if (other_user_id > 0) {
+    fprintf(o_out, "&other_user_id=%d", other_user_id);
+  }
+  if (marked_str && *marked_str) {
+    fprintf(o_out, "&marked=%s", marked_str);
+  }
+  fclose(o_out); o_out = 0;
+
+  if (o_str && *o_str) {
+    ss_url_unescaped(url, sizeof(url), phr, SSERV_CMD_HTTP_REQUEST, new_op, "%s", o_str);
+  } else {
+    ss_url_unescaped(url, sizeof(url), phr, SSERV_CMD_HTTP_REQUEST, new_op, 0);
+  }
+
+  xfree(o_str); o_str = 0; o_len = 0;
+
+  fprintf(fout, "Content-Type: text/html; charset=%s\nCache-Control: no-cache\nPragma: no-cache\nLocation: %s\n\n", EJUDGE_CHARSET, url);
+}
+
+static unsigned char *
+fix_string(const unsigned char *s)
+{
+  if (!s) return NULL;
+
+  int len = strlen(s);
+  if (len < 0) return NULL;
+
+  while (len > 0 && (s[len - 1] <= ' ' || s[len - 1] == 127)) --len;
+  if (len <= 0) return xstrdup("");
+
+  int i = 0;
+  while (i < len && (s[i] <= ' ' || s[i] == 127)) ++i;
+  if (i >= len) return xstrdup("");
+
+  unsigned char *out = (unsigned char *) xmalloc(len + 1);
+  int j = 0;
+  for (; i < len; ++i, ++j) {
+    if (s[i] <= ' ' || s[i] == 127) {
+      out[j] = ' ';
+    } else {
+      out[j] = s[i];
+    }
+  }
+  out[j] = 0;
+
+  return out;
+}
+
+static void
+ss_select(
+        FILE *fout,
+        const unsigned char *param,
+        const unsigned char **options,
+        int value)
+{
+  int option_count = 0, i;
+  struct html_armor_buffer ab = HTML_ARMOR_INITIALIZER;
+  const unsigned char *s;
+
+  if (!options) return;
+  for (; options[option_count]; ++option_count);
+  if (option_count <= 0) return;
+
+  if (value < 0 || value >= option_count) value = 0;
+
+  fprintf(fout, "<select name=\"%s\">", param);
+  for (i = 0; i < option_count; ++i) {
+    s = "";
+    if (i == value) s = " selected=\"selected\"";
+    fprintf(fout, "<option value=\"%d\"%s>%s</option>",
+            i, s, ARMOR(options[i]));
+  }
+  fprintf(fout, "</select>");
+  html_armor_free(&ab);
+}
+
+static int
+get_global_caps(const struct super_http_request_info *phr, opcap_t *pcap)
+{
+  return opcaps_find(&phr->config->capabilities, phr->login, pcap);
+}
+static int
+get_contest_caps(const struct super_http_request_info *phr, const struct contest_desc *cnts, opcap_t *pcap)
+{
+  return opcaps_find(&cnts->capabilities, phr->login, pcap);
+}
+
+static int
+is_globally_privileged(const struct super_http_request_info *phr, const struct userlist_user *u)
+{
+  opcap_t caps = 0;
+  if (u->is_privileged) return 1;
+  if (opcaps_find(&phr->config->capabilities, u->login, &caps) >= 0) return 1;
+  return 0;
+}
+static int
+is_contest_privileged(
+        const struct contest_desc *cnts,
+        const struct userlist_user *u)
+{
+  opcap_t caps = 0;
+  if (opcaps_find(&cnts->capabilities, u->login, &caps) >= 0) return 1;
+  return 0;
+}
+static int
+is_privileged(
+        const struct super_http_request_info *phr,
+        const struct contest_desc *cnts,
+        const struct userlist_user *u)
+{
+  opcap_t caps = 0;
+  if (u->is_privileged) return 1;
+  if (opcaps_find(&phr->config->capabilities, u->login, &caps) >= 0) return 1;
+  if (!cnts) return 0;
+  if (opcaps_find(&cnts->capabilities, u->login, &caps) >= 0) return 1;
+  return 0;
+}
+
+static int
+ss_parse_params(
+        struct super_http_request_info *phr,
+        const struct meta_methods *mth,
+        void *params)
+{
+  int field_id, field_type;
+  void *field_ptr;
+  const unsigned char *field_name;
+
+  for (field_id = 1; field_id < mth->last_tag; ++field_id) {
+    field_type = mth->get_type(field_id);
+    field_ptr = mth->get_ptr_nc(params, field_id);
+    field_name = mth->get_name(field_id);
+    if (!field_ptr || !field_name) continue;
+    switch (field_type) {
+    case '0':                   /* ej_int_opt_0_t */
+      ss_cgi_param_int_opt(phr, field_name, (int*) field_ptr, 0);
+      break;
+    case '1':                   /* ej_textbox_t */
+      {
+        const unsigned char *s = 0;
+        if (ss_cgi_param(phr, field_name, &s) <= 0 || !s || !*s) {
+          return -S_ERR_INV_VALUE;
+        }
+        unsigned char *s2 = fix_string(s);
+        if (!s2 || !*s2) {
+          xfree(s2);
+          return -S_ERR_INV_VALUE;
+        }
+        *(unsigned char **) field_ptr = s2;
+      }
+      break;
+    case '2':                   /* ej_textbox_opt_t */
+      {
+        const unsigned char *s = 0;
+        if (ss_cgi_param(phr, field_name, &s) < 0) {
+          return -S_ERR_INV_VALUE;
+        }
+        unsigned char *s2 = fix_string(s);
+        if (!s2) s2 = xstrdup("");
+        *(unsigned char **) field_ptr = s2;
+      }
+      break;
+    case '3':                   /* ej_checkbox_t */
+      {
+        int *ip = (int*) field_ptr;
+        ss_cgi_param_int_opt(phr, field_name, ip, 0);
+        if (*ip != 1) *ip = 0;
+      }
+      break;
+    case '4':                   /* ej_int_opt_1_t */
+      ss_cgi_param_int_opt(phr, field_name, (int*) field_ptr, 1);
+      break;
+    case '5':                   /* ej_int_opt_m1_t */
+      ss_cgi_param_int_opt(phr, field_name, (int*) field_ptr, -1);
+      break;
+    default:
+      abort();
+    }
+  }
+
+  return 0;
+}
+
+static int
+userlist_user_count_contests(struct userlist_user *u)
+{
+  struct userlist_contest *c;
+  int tot = 0;
+
+  if (!u || !u->contests) return 0;
+  for (c = FIRST_CONTEST(u); c; c = NEXT_CONTEST(c), tot++);
+  return tot;
+}
+static int
+userlist_user_count_cookies(struct userlist_user *u)
+{
+  struct userlist_cookie *cookie;
+  int tot = 0;
+
+  if (!u) return 0;
+  if (!u->cookies) return 0;
+  for (cookie = FIRST_COOKIE(u); cookie; cookie = NEXT_COOKIE(cookie), tot++);
+  return tot;
+}
+
+static struct userlist_user *
+get_user_info(
+        struct super_http_request_info *phr,
+        int user_id,
+        int contest_id)
+{
+  unsigned char *xml_text = 0;
+  struct userlist_user *u = 0;
+
+  if (userlist_clnt_get_info(phr->userlist_clnt, ULS_PRIV_GET_USER_INFO,
+                             user_id, contest_id, &xml_text) < 0 || !xml_text) {
+    return NULL;
+  }
+  u = userlist_parse_user_str(xml_text);
+  xfree(xml_text);
+  return u;
+}
+
+static void
+print_top_navigation_links(
+        FILE *log_f,
+        FILE *out_f,
+        struct super_http_request_info *phr,
+        int contest_id,
+        int group_id,
+        int other_user_id,
+        const unsigned char *marked_str)
+{
+  unsigned char hbuf[1024];
+  unsigned char contest_id_str[1024];
+  unsigned char group_id_str[1024];
+  unsigned char *marked_param = "";
+
+  contest_id_str[0] = 0;
+  if (contest_id > 0) {
+    snprintf(contest_id_str, sizeof(contest_id_str), "&amp;contest_id=%d", contest_id);
+  }
+  if (group_id < 0) group_id = 0;
+  group_id_str[0] = 0;
+  if (group_id > 0) {
+    snprintf(group_id_str, sizeof(group_id_str), "&amp;group_id=%d", group_id);
+  }
+  if (marked_str && *marked_str) {
+    int len = strlen(marked_str);
+    if (len < 100000) {
+      marked_param = alloca(len + 32);
+      sprintf(marked_param, "&amp;marked=%s", marked_str);
+    }
+  }
+
+  fprintf(out_f, "<ul>");
+  fprintf(out_f, "<li>%s%s</a></li>",
+          html_hyperref(hbuf, sizeof(hbuf), phr->session_id, phr->self_url,
+                        NULL, NULL),
+          "Main page");
+  fprintf(out_f, "<li>%s%s</a></li>",
+          html_hyperref(hbuf, sizeof(hbuf), phr->session_id, phr->self_url,
+                        NULL, "action=%d&amp;op=%d%s",
+                        SSERV_CMD_HTTP_REQUEST, SSERV_OP_USER_BROWSE_PAGE, marked_param),
+          "Browse users");
+  fprintf(out_f, "<li>%s%s</a></li>",
+          html_hyperref(hbuf, sizeof(hbuf), phr->session_id, phr->self_url,
+                        NULL, "action=%d&amp;op=%d",
+                        SSERV_CMD_HTTP_REQUEST, SSERV_OP_GROUP_BROWSE_PAGE),
+          "Browse groups");
+  if (contest_id > 0) {
+    fprintf(out_f, "<li>%s%s %d</a></li>",
+            html_hyperref(hbuf, sizeof(hbuf), phr->session_id, phr->self_url,
+                          NULL, "action=%d&amp;op=%d%s%s",
+                          SSERV_CMD_HTTP_REQUEST, SSERV_OP_USER_BROWSE_PAGE,
+                          contest_id_str, marked_param),
+            "Browse users of contest", contest_id);
+  }
+  if (group_id > 0) {
+    fprintf(out_f, "<li>%s%s %d</a></li>",
+            html_hyperref(hbuf, sizeof(hbuf), phr->session_id, phr->self_url,
+                          NULL, "action=%d&amp;op=%d%s%s",
+                          SSERV_CMD_HTTP_REQUEST, SSERV_OP_USER_BROWSE_PAGE,
+                          group_id_str, marked_param),
+            "Browse users of group", group_id);
+  }
+  if (other_user_id > 0) {
+    fprintf(out_f, "<li>%s%s</a></li>",
+            html_hyperref(hbuf, sizeof(hbuf), phr->session_id, phr->self_url,
+                          NULL, "action=%d&amp;op=%d&amp;other_user_id=%d%s%s",
+                          SSERV_CMD_HTTP_REQUEST, SSERV_OP_USER_DETAIL_PAGE,
+                          other_user_id,
+                          contest_id_str, group_id_str),
+            "User details");
+  }
+  fprintf(out_f, "</ul>\n");
+}
+
+static const unsigned char * const reg_status_strs[] =
+{
+  "<font color=\"green\">OK</font>",
+  "<font color=\"magenta\">Pending</font>",
+  "<font color=\"red\">Rejected</font>",
+  "<font color=\"red\"><b>Invalid status</b></font>",
+};
+static const unsigned char * const flag_op_legends[] =
+{
+  "Do nothing", "Clear", "Set", "Toggle", NULL,
+};
+
+int
+super_serve_op_USER_BROWSE_PAGE(
+        FILE *log_f,
+        FILE *out_f,
+        struct super_http_request_info *phr)
+{
+  int retval = 0, r;
+  unsigned char buf[1024];
+  unsigned char hbuf[1024];
+  unsigned char *xml_text = 0;
+  const unsigned char *user_filter = 0;
+  int user_offset = 0;
+  int user_count = 20;
+  const unsigned char *s;
+  struct userlist_list *users = 0;
+  int user_id, serial;
+  const struct userlist_user *u;
+  struct html_armor_buffer ab = HTML_ARMOR_INITIALIZER;
+  const unsigned char *cl;
+  int contest_id = 0, group_id = 0;
+  unsigned char contest_id_str[128];
+  unsigned char group_id_str[128];
+  const struct contest_desc *cnts = 0;
+  opcap_t gcaps = 0;
+  opcap_t caps = 0;
+  const struct userlist_contest *reg = 0;
+  int min_user_id = INT_MAX;
+  int max_user_id = 0;
+  bitset_t marked = BITSET_INITIALIZER;
+  unsigned char *marked_str = 0;
+
+  ss_cgi_param_int_opt(phr, "contest_id", &contest_id, 0);
+  ss_cgi_param_int_opt(phr, "group_id", &group_id, 0);
+
+  s = 0;
+  if (ss_cgi_param(phr, "marked", &s) > 0 && s) {
+    if (bitset_url_decode(s, &marked) >= 0) {
+      marked_str = bitset_url_encode(&marked);
+    }
+  }
+
+  if (contest_id < 0) contest_id = 0;
+  if (contest_id > 0) {
+    if (contests_get(contest_id, &cnts) < 0 || !cnts) contest_id = 0;
+  }
+  contest_id_str[0] = 0;
+  if (contest_id > 0) {
+    snprintf(contest_id_str, sizeof(contest_id_str), "&amp;contest_id=%d", contest_id);
+  }
+  if (group_id < 0) group_id = 0;
+  group_id_str[0] = 0;
+  if (group_id > 0) {
+    snprintf(group_id_str, sizeof(group_id_str), "&amp;group_id=%d", group_id);
+  }
+
+  if (get_global_caps(phr, &gcaps) >= 0 && opcaps_check(gcaps, OPCAP_LIST_USERS) >= 0) {
+    // this user can view the full user list and the user list for any contest
+  } else if (!cnts) {
+    // user without global OPCAP_LIST_USERS capability cannot view the full user list
+    FAIL(S_ERR_PERM_DENIED);
+  } else if (get_contest_caps(phr, cnts, &caps) < 0 || opcaps_check(caps, OPCAP_LIST_USERS) < 0) {
+    FAIL(S_ERR_PERM_DENIED);
+  }
+
+  hbuf[0] = 0;
+  if (contest_id > 0 && group_id > 0) {
+    snprintf(hbuf, sizeof(hbuf), " for contest %d, group %d", contest_id, group_id);
+  } else if (contest_id > 0) {
+    snprintf(hbuf, sizeof(hbuf), " for contest %d", contest_id);
+  } else if (group_id > 0) {
+    snprintf(hbuf, sizeof(hbuf), " for group %d", group_id);
+  }
+  snprintf(buf, sizeof(buf), "serve-control: %s, browsing users%s",
+           phr->html_name, hbuf);
+  ss_write_html_header(out_f, phr, buf, 0, NULL);
+
+  fprintf(out_f, "<script language=\"javascript\">\n");
+  fprintf(out_f,
+          "function setAllCheckboxes(value)\n"
+          "{\n"
+          "  objs = document.forms[1].elements;\n"
+          "  if (objs != null) {\n"
+          "    for (var i = 0; i < objs.length; ++i) {\n"
+          "      if (objs[i].type == \"checkbox\") {\n"
+          "        objs[i].checked = value;\n"
+          "      }\n"
+          "    }\n"
+          "  }\n"
+          "}\n"
+          "function toggleAllCheckboxes()\n"
+          "{\n"
+          "  objs = document.forms[1].elements;\n"
+          "  if (objs != null) {\n"
+          "    for (var i = 0; i < objs.length; ++i) {\n"
+          "      if (objs[i].type == \"checkbox\") {\n"
+          "        objs[i].checked = !objs[i].checked;\n"
+          "      }\n"
+          "    }\n"
+          "  }\n"
+          "}\n"
+          "function setOperationVisibility(oper, value)\n"
+          "{\n"
+          "  obj1 = document.getElementById(\"Show\" + oper + \"Menu\");\n"
+          "  obj2 = document.getElementById(\"Hide\" + oper + \"Menu\");\n"
+          "  if (value) {\n"
+          "    obj1.style.display = \"none\";\n"
+          "    obj2.style.display = \"\";\n"
+          "  } else {\n"
+          "    obj1.style.display = \"\";\n"
+          "    obj2.style.display = \"none\";\n"
+          "  }\n"
+          "}\n"
+          "");
+  fprintf(out_f, "</script>\n");
+
+  fprintf(out_f, "<h1>%s</h1>\n", buf);
+
+  if (cnts) {
+    fprintf(out_f, "<h2>Contest %d: %s</h2>", cnts->id, ARMOR(cnts->name));
+  } else {
+    fprintf(out_f, "<br/>\n");
+  }
+
+  print_top_navigation_links(log_f, out_f, phr, contest_id, group_id, 0, marked_str);
+
+  if (!phr->userlist_clnt) {
+    fprintf(out_f, "<hr/><h2>Error</h2>\n");
+    fprintf(out_f, "<pre>No connection to the server!</pre>\n");
+    goto do_footer;
+  }
+
+  if (phr->ss->user_filter_set) {
+    user_filter = phr->ss->user_filter;
+    user_offset = phr->ss->user_offset;
+    user_count = phr->ss->user_count;
+  }
+
+  html_start_form(out_f, 1, phr->self_url, "");
+  html_hidden(out_f, "SID", "%016llx", phr->session_id);
+  html_hidden(out_f, "action", "%d", SSERV_CMD_HTTP_REQUEST);
+  if (contest_id > 0) {
+    html_hidden(out_f, "contest_id", "%d", contest_id);
+  }
+  if (group_id > 0) {
+    html_hidden(out_f, "group_id", "%d", group_id);
+  }
+  if (marked_str && marked_str) {
+    html_hidden(out_f, "marked", "%s", marked_str);
+  }
+  fprintf(out_f, "<table class=\"b0\">");
+  s = user_filter;
+  if (!s) s = "";
+  fprintf(out_f, "<!--<tr><td class=\"b0\">Filter:</td><td class=\"b0\">%s</td></tr>-->",
+          html_input_text(buf, sizeof(buf), "user_filter", 50, "%s", ARMOR(s)));
+  hbuf[0] = 0;
+  if (phr->ss->user_filter_set) {
+    snprintf(hbuf, sizeof(hbuf), "%d", user_offset);
+  }
+  fprintf(out_f, "<tr><td class=\"b0\">Offset:</td><td class=\"b0\">%s</td></tr>",
+          html_input_text(buf, sizeof(buf), "user_offset", 10, "%s", hbuf));
+  hbuf[0] = 0;
+  if (phr->ss->user_filter_set) {
+    snprintf(hbuf, sizeof(hbuf), "%d", user_count);
+  }
+  fprintf(out_f, "<tr><td class=\"b0\">Count:</td><td class=\"b0\">%s</td></tr>",
+          html_input_text(buf, sizeof(buf), "user_count", 10, "%s", hbuf));
+  fprintf(out_f, "<tr><td class=\"b0\">&nbsp;</td><td class=\"b0\"><input type=\"submit\" name=\"op_%d\" value=\"%s\" /></td></tr>",
+          SSERV_OP_USER_FILTER_CHANGE_ACTION, "Change");
+  fprintf(out_f, "</table>");
+  fprintf(out_f, "<table class=\"b0\"><tr>");
+  fprintf(out_f, "<td class=\"b0\"><input type=\"submit\" name=\"op_%d\" value=\"%s\" /></td>", SSERV_OP_USER_FILTER_FIRST_PAGE_ACTION, "&lt;&lt;");
+  fprintf(out_f, "<td class=\"b0\"><input type=\"submit\" name=\"op_%d\" value=\"%s\" /></td>", SSERV_OP_USER_FILTER_PREV_PAGE_ACTION, "&lt;");
+  fprintf(out_f, "<td class=\"b0\"><input type=\"submit\" name=\"op_%d\" value=\"%s\" /></td>", SSERV_OP_USER_FILTER_NEXT_PAGE_ACTION, "&gt;");
+  fprintf(out_f, "<td class=\"b0\"><input type=\"submit\" name=\"op_%d\" value=\"%s\" /></td>", SSERV_OP_USER_FILTER_LAST_PAGE_ACTION, "&gt;&gt;");
+  fprintf(out_f, "</tr></table>\n");
+  //fprintf(out_f, "</form>\n");
+
+  cl = " class=\"b0\"";
+  fprintf(out_f, "<table%s>", cl);
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td>", cl, "Jump to contest");
+  hbuf[0] = 0;
+  if (contest_id > 0) {
+    snprintf(hbuf, sizeof(hbuf), "%d", contest_id);
+  }
+  fprintf(out_f, "<td%s>%s</td>", cl,
+          html_input_text(buf, sizeof(buf), "jump_contest_id", 10, "%s", hbuf));
+  fprintf(out_f, "<td%s><input type=\"submit\" name=\"op_%d\" value=\"%s\" /></td></tr>",
+          cl, SSERV_OP_USER_JUMP_CONTEST_ACTION, "Jump");
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td>", cl, "Jump to group");
+  hbuf[0] = 0;
+  if (group_id > 0) {
+    snprintf(hbuf, sizeof(hbuf), "%d", group_id);
+  }
+  fprintf(out_f, "<td%s>%s</td>", cl,
+          html_input_text(buf, sizeof(buf), "jump_group_id", 10, "%s", hbuf));
+  fprintf(out_f, "<td%s><input type=\"submit\" name=\"op_%d\" value=\"%s\" /></td></tr>",
+          cl, SSERV_OP_USER_JUMP_GROUP_ACTION, "Jump");
+  fprintf(out_f, "</table>\n");
+
+  r = userlist_clnt_list_users_2(phr->userlist_clnt, ULS_LIST_ALL_USERS_2,
+                                 contest_id, group_id, user_filter, user_offset, user_count,
+                                 &xml_text);
+  if (r < 0) {
+    fprintf(out_f, "</form>\n");
+    fprintf(out_f, "<hr/><h2>Error</h2>\n");
+    fprintf(out_f, "<pre>Cannot get user list: %s</pre>\n",
+            userlist_strerror(-r));
+    goto do_footer;
+  }
+  users = userlist_parse_str(xml_text);
+  if (!users) {
+    fprintf(out_f, "</form>\n");
+    fprintf(out_f, "<hr/><h2>Error</h2>\n");
+    fprintf(out_f, "<pre>XML parse error</pre>\n");
+    goto do_footer;
+  }
+
+  for (user_id = 1; user_id < users->user_map_size; ++user_id) {
+    if (!(u = users->user_map[user_id])) continue;
+    if (user_id >= max_user_id) max_user_id = user_id;
+    if (user_id <= min_user_id) min_user_id = user_id;
+  }
+
+  //html_start_form(out_f, 1, phr->self_url, "");
+  /*
+  html_hidden(out_f, "SID", "%016llx", phr->session_id);
+  html_hidden(out_f, "action", "%d", SSERV_CMD_HTTP_REQUEST);
+  if (contest_id > 0) {
+    html_hidden(out_f, "contest_id", "%d", contest_id);
+  }
+  if (group_id > 0) {
+    html_hidden(out_f, "group_id", "%d", group_id);
+  }
+  */
+  html_hidden(out_f, "min_user_id", "%d", min_user_id);
+  html_hidden(out_f, "max_user_id", "%d", max_user_id);
+  cl = " class=\"b1\"";
+  fprintf(out_f, "<table%s>\n", cl);
+
+  fprintf(out_f, "<tr>");
+  fprintf(out_f, "<th%s>&nbsp;</th>", cl);
+  fprintf(out_f, "<th%s>NN</th>", cl);
+  fprintf(out_f, "<th%s>User Id</th>", cl);
+  fprintf(out_f, "<th%s>User Login</th>", cl);
+  fprintf(out_f, "<th%s>E-mail</th>", cl);
+  fprintf(out_f, "<th%s>Name</th>", cl);
+  //fprintf(out_f, "<th%s>Flags</th>", cl);
+  if (contest_id > 0) {
+    fprintf(out_f, "<th%s>Status</th>", cl);
+    fprintf(out_f, "<th%s>Flags</th>", cl);
+  }
+  fprintf(out_f, "<th%s>Operations</th>", cl);
+  fprintf(out_f, "</tr>\n");
+
+  serial = user_offset - 1;
+  for (user_id = 1; user_id < users->user_map_size; ++user_id) {
+    if (!(u = users->user_map[user_id])) continue;
+    reg = 0;
+    if (contest_id > 0) {
+      reg = userlist_get_user_contest(u, contest_id);
+    }
+
+    ++serial;
+    fprintf(out_f, "<tr>\n");
+    s = "";
+    if (bitset_safe_get(&marked, user_id)) {
+      s = " checked=\"checked\"";
+    }
+    fprintf(out_f, "<td class=\"b1\"><input type=\"checkbox\" name=\"user_%d\" value=\"1\"%s/></td>", user_id, s);
+    fprintf(out_f, "<td class=\"b1\">%d</td>", serial);
+    fprintf(out_f, "<td class=\"b1\">%d</td>", user_id);
+    if (!u->login) {
+      fprintf(out_f, "<td class=\"b1\"><i>NULL</i></td>");
+    } else {
+      fprintf(out_f, "<td class=\"b1\"><tt>%s</tt></td>", ARMOR(u->login));
+    }
+    if (!u->email) {
+      fprintf(out_f, "<td class=\"b1\"><i>NULL</i></td>");
+    } else {
+      fprintf(out_f, "<td class=\"b1\"><tt>%s</tt></td>", ARMOR(u->email));
+    }
+    if (!u->cnts0 || !u->cnts0->name) {
+      fprintf(out_f, "<td class=\"b1\"><i>NULL</i></td>");
+    } else {
+      fprintf(out_f, "<td class=\"b1\"><tt>%s</tt></td>", ARMOR(u->cnts0->name));
+    }
+    /*
+    int flags_count = 0;
+    fprintf(out_f, "<td class=\"b1\">");
+    if (u->is_privileged) {
+      if (flags_count > 0) fprintf(out_f, ", ");
+      fprintf(out_f, "privileged");
+      ++flags_count;
+    }
+    if (u->is_invisible) {
+      if (flags_count > 0) fprintf(out_f, ", ");
+      fprintf(out_f, "invisible");
+      ++flags_count;
+    }
+    if (u->is_banned) {
+      if (flags_count > 0) fprintf(out_f, ", ");
+      fprintf(out_f, "banned");
+      ++flags_count;
+    }
+    if (u->is_locked) {
+      if (flags_count > 0) fprintf(out_f, ", ");
+      fprintf(out_f, "locked");
+      ++flags_count;
+    }
+    if (u->show_login) {
+      if (flags_count > 0) fprintf(out_f, ", ");
+      fprintf(out_f, "show_login");
+      ++flags_count;
+    }
+    if (u->show_email) {
+      if (flags_count > 0) fprintf(out_f, ", ");
+      fprintf(out_f, "show_email");
+      ++flags_count;
+    }
+    if (u->read_only) {
+      if (flags_count > 0) fprintf(out_f, ", ");
+      fprintf(out_f, "read_only");
+      ++flags_count;
+    }
+    if (u->never_clean) {
+      if (flags_count > 0) fprintf(out_f, ", ");
+      fprintf(out_f, "never_clean");
+      ++flags_count;
+    }
+    if (u->simple_registration) {
+      if (flags_count > 0) fprintf(out_f, ", ");
+      fprintf(out_f, "simple_reg");
+      ++flags_count;
+    }
+    if (!flags_count) {
+      fprintf(out_f, "&nbsp;");
+    }
+    fprintf(out_f, "</td>");
+    */
+
+    if (contest_id > 0) {
+      if (reg) {
+        r = reg->status;
+        if (r < 0 || r >= USERLIST_REG_LAST) r = USERLIST_REG_LAST;
+        fprintf(out_f, "<td%s>%s</td>", cl, reg_status_strs[r]);
+        fprintf(out_f, "<td%s>", cl);
+        r = 0;
+        if ((reg->flags & USERLIST_UC_INVISIBLE)) {
+          if (r++) fprintf(out_f, ", ");
+          fprintf(out_f, "invisible");
+        }
+        if ((reg->flags & USERLIST_UC_BANNED)) {
+          if (r++) fprintf(out_f, ", ");
+          fprintf(out_f, "banned");
+        }
+        if ((reg->flags & USERLIST_UC_LOCKED)) {
+          if (r++) fprintf(out_f, ", ");
+          fprintf(out_f, "locked");
+        }
+        if ((reg->flags & USERLIST_UC_INCOMPLETE)) {
+          if (r++) fprintf(out_f, ", ");
+          fprintf(out_f, "incomplete");
+        }
+        if ((reg->flags & USERLIST_UC_DISQUALIFIED)) {
+          if (r++) fprintf(out_f, ", ");
+          fprintf(out_f, "disqualified");
+        }
+        fprintf(out_f, "</td>");
+      } else {
+        fprintf(out_f, "<td%s>&nbsp;</td><td%s>&nbsp;</td>", cl, cl);
+      }
+    }
+
+    fprintf(out_f, "<td%s>", cl);
+    fprintf(out_f, "%s%s</a>",
+            html_hyperref(hbuf, sizeof(hbuf), phr->session_id, phr->self_url,
+                          NULL, "action=%d&amp;op=%d&amp;other_user_id=%d%s%s",
+                          SSERV_CMD_HTTP_REQUEST, SSERV_OP_USER_DETAIL_PAGE,
+                          user_id, contest_id_str, group_id_str),
+            "[Details]");
+    fprintf(out_f, "&nbsp;%s%s</a>",
+            html_hyperref(hbuf, sizeof(hbuf), phr->session_id, phr->self_url,
+                          NULL, "action=%d&amp;op=%d&amp;next_op=%d&amp;other_user_id=%d%s%s",
+                          SSERV_CMD_HTTP_REQUEST, SSERV_OP_USER_PASSWORD_PAGE,
+                          SSERV_OP_USER_BROWSE_PAGE,
+                          user_id, contest_id_str, group_id_str),
+            "[Reg. password]");
+    if (contest_id > 0 && cnts && !cnts->disable_team_password) {
+      fprintf(out_f, "&nbsp;%s%s</a>",
+              html_hyperref(hbuf, sizeof(hbuf), phr->session_id, phr->self_url,
+                            NULL, "action=%d&amp;op=%d&amp;next_op=%d&amp;other_user_id=%d%s%s",
+                            SSERV_CMD_HTTP_REQUEST, SSERV_OP_USER_CNTS_PASSWORD_PAGE,
+                            SSERV_OP_USER_BROWSE_PAGE,
+                            user_id, contest_id_str, group_id_str),
+              "[Cnts. password]");
+    }
+    if (contest_id > 0) {
+      fprintf(out_f, "&nbsp;%s[%s]</a>",
+              html_hyperref(hbuf, sizeof(hbuf), phr->session_id, phr->self_url,
+                            NULL, "action=%d&amp;op=%d&amp;next_op=%d&amp;other_user_id=%d&amp;other_contest_id=%d%s%s",
+                            SSERV_CMD_HTTP_REQUEST, SSERV_OP_USER_EDIT_REG_PAGE,
+                            SSERV_OP_USER_BROWSE_PAGE,
+                            user_id, contest_id, contest_id_str, group_id_str),
+              "Change");
+      fprintf(out_f, "&nbsp;%s[%s]</a>",
+              html_hyperref(hbuf, sizeof(hbuf), phr->session_id, phr->self_url,
+                            NULL, "action=%d&amp;op=%d&amp;next_op=%d&amp;other_user_id=%d&amp;other_contest_id=%d%s%s",
+                            SSERV_CMD_HTTP_REQUEST, SSERV_OP_USER_DELETE_REG_PAGE,
+                            SSERV_OP_USER_BROWSE_PAGE,
+                            user_id, contest_id, contest_id_str, group_id_str),
+              "Delete");
+    }
+    fprintf(out_f, "</td>");
+    fprintf(out_f, "</tr>\n");
+  }
+  fprintf(out_f, "</table>\n");
+
+  cl = " class=\"b0\"";
+  fprintf(out_f, "<table%s><tr>", cl);
+  if (cnts) {
+    fprintf(out_f, "<td%s><input type=\"submit\" name=\"op_%d\" value=\"%s\" /></td>",
+            cl, SSERV_OP_USER_BROWSE_MARK_ALL_ACTION, "Mark all");
+  }
+  fprintf(out_f, "<td%s><input type=\"submit\" name=\"op_%d\" value=\"%s\" /></td>",
+          cl, SSERV_OP_USER_BROWSE_UNMARK_ALL_ACTION, "Unmark all");
+  if (cnts) {
+    fprintf(out_f, "<td%s><input type=\"submit\" name=\"op_%d\" value=\"%s\" /></td>",
+            cl, SSERV_OP_USER_BROWSE_TOGGLE_ALL_ACTION, "Toggle all");
+  }
+  fprintf(out_f, "</tr></table>\n");
+
+  fprintf(out_f, "<div id=\"ShowRegistrationMenu\">");
+  fprintf(out_f, "<a onclick=\"setOperationVisibility('Registration', true)\">[%s]</a>\n",
+          "Registration operations");
+  fprintf(out_f, "</div>");
+  fprintf(out_f, "<div style=\"display: none;\" id=\"HideRegistrationMenu\">");
+  fprintf(out_f, "<a onclick=\"setOperationVisibility('Registration', false)\">[%s]</a><br/>\n",
+          "Hide registration operations");
+  fprintf(out_f, "<table%s>", cl);
+  fprintf(out_f, "<tr><td%s colspan=\"2\"><b>Contest ID:</b> <input type=\"text\" name=\"other_contest_id\" /></td></tr>\n", cl);
+  fprintf(out_f, "<tr><td%s><input type=\"submit\" name=\"op_%d\" value=\"%s\" /></td>",
+          cl, SSERV_OP_USER_SEL_CREATE_REG_PAGE, "Register for another contest");
+  if (cnts) {
+    fprintf(out_f, "<td%s><input type=\"submit\" name=\"op_%d\" value=\"%s\" /></td>",
+            cl, SSERV_OP_USER_SEL_CREATE_REG_AND_COPY_PAGE, "Register for another contest and copy data");
+  }
+  fprintf(out_f, "</tr></table>\n");
+  fprintf(out_f, "</div>");
+
+  fprintf(out_f, "<div id=\"ShowGroupMenu\">");
+  fprintf(out_f, "<a onclick=\"setOperationVisibility('Group', true)\">[%s]</a>\n",
+          "Group operations");
+  fprintf(out_f, "</div>");
+  fprintf(out_f, "<div style=\"display: none;\" id=\"HideGroupMenu\">");
+  fprintf(out_f, "<a onclick=\"setOperationVisibility('Group', false)\">[%s]</a><br/>\n",
+          "Hide group operations");
+  fprintf(out_f, "<table%s>", cl);
+  fprintf(out_f, "<tr><td%s colspan=\"2\"><b>Group ID:</b> <input type=\"text\" name=\"other_group_id\" /></td></tr>\n", cl);
+  fprintf(out_f, "<tr><td%s><input type=\"submit\" name=\"op_%d\" value=\"%s\" /></td>",
+          cl, SSERV_OP_USER_SEL_CREATE_GROUP_MEMBER_PAGE, "Add users to another group");
+  if (group_id > 0) {
+    fprintf(out_f, "<td%s><input type=\"submit\" name=\"op_%d\" value=\"%s\" /></td>",
+            cl, SSERV_OP_USER_SEL_DELETE_GROUP_MEMBER_PAGE, "Remove from group");
+  }
+  fprintf(out_f, "</tr></table>\n");
+  fprintf(out_f, "</div>");
+
+  if (cnts) {
+    fprintf(out_f, "<div id=\"ShowPasswordMenu\">");
+    fprintf(out_f, "<a onclick=\"setOperationVisibility('Password', true)\">[%s]</a>\n", "Password operations");
+    fprintf(out_f, "</div>");
+    fprintf(out_f, "<div style=\"display: none;\" id=\"HidePasswordMenu\">");
+    fprintf(out_f, "<a onclick=\"setOperationVisibility('Password', false)\">[%s]</a><br/>\n", "Hide password operations");
+    cl = " class=\"b0\"";
+    fprintf(out_f, "<table%s>", cl);
+    fprintf(out_f, "<tr><td%s><b>Registration passwords:</b></td>", cl);
+    fprintf(out_f, "<td%s><input type=\"submit\" name=\"op_%d\" value=\"%s\" /></td>",
+            cl, SSERV_OP_USER_SEL_VIEW_PASSWD_REDIRECT, "View");
+    fprintf(out_f, "<td%s>&nbsp;</td>", cl);
+    fprintf(out_f, "<td%s><input type=\"submit\" name=\"op_%d\" value=\"%s\" /></td>",
+            cl, SSERV_OP_USER_SEL_RANDOM_PASSWD_PAGE, "Generate random");
+    fprintf(out_f, "</tr>\n");
+    if (!cnts->disable_team_password) {
+      fprintf(out_f, "<tr><td%s><b>Contest passwords:</b></td>", cl);
+      fprintf(out_f, "<td%s><input type=\"submit\" name=\"op_%d\" value=\"%s\" /></td>",
+              cl, SSERV_OP_USER_SEL_VIEW_CNTS_PASSWD_REDIRECT, "View");
+      fprintf(out_f, "<td%s><input type=\"submit\" name=\"op_%d\" value=\"%s\" /></td>",
+              cl, SSERV_OP_USER_SEL_CLEAR_CNTS_PASSWD_PAGE, "Clear");
+      fprintf(out_f, "<td%s><input type=\"submit\" name=\"op_%d\" value=\"%s\" /></td>",
+              cl, SSERV_OP_USER_SEL_RANDOM_CNTS_PASSWD_PAGE, "Generate random");
+      fprintf(out_f, "</tr>\n");
+    }
+    fprintf(out_f, "</table>\n");
+    fprintf(out_f, "</div>");
+
+    fprintf(out_f, "<div id=\"ShowStatusMenu\">");
+    fprintf(out_f, "<a onclick=\"setOperationVisibility('Status', true)\">[%s]</a>\n", "Status operations");
+    fprintf(out_f, "</div>");
+    fprintf(out_f, "<div style=\"display: none;\" id=\"HideStatusMenu\">");
+    fprintf(out_f, "<a onclick=\"setOperationVisibility('Status', false)\">[%s]</a><br/>\n", "Hide status operations");
+    cl = " class=\"b0\"";
+    fprintf(out_f, "<table%s>", cl);
+    fprintf(out_f, "<tr><td%s><input type=\"submit\" name=\"op_%d\" value=\"%s\" /></td></tr>",
+            cl, SSERV_OP_USER_SEL_DELETE_REG_PAGE, "Delete registrations");
+    fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>", cl, "Status", cl);
+    ss_select(out_f, "status", (const unsigned char* []) { "OK", "Pending", "Rejected", NULL }, 0);
+    fprintf(out_f, "</td>");
+    fprintf(out_f, "<td%s><input type=\"submit\" name=\"op_%d\" value=\"%s\" /></td></tr>",
+            cl, SSERV_OP_USER_SEL_CHANGE_REG_STATUS_PAGE, "Change status");
+    fprintf(out_f, "</table>\n");
+    fprintf(out_f, "</div>");
+
+    fprintf(out_f, "<div id=\"ShowFlagsMenu\">");
+    fprintf(out_f, "<a onclick=\"setOperationVisibility('Flags', true)\">[%s]</a>\n", "Flags operations");
+    fprintf(out_f, "</div>");
+    fprintf(out_f, "<div style=\"display: none;\" id=\"HideFlagsMenu\">");
+    fprintf(out_f, "<a onclick=\"setOperationVisibility('Flags', false)\">[%s]</a><br/>\n", "Hide flags operations");
+
+    cl = " class=\"b0\"";
+    fprintf(out_f, "<table%s>", cl);
+
+    static const unsigned char * const flag_vars[] =
+    {
+      "invisible_op", "banned_op", "locked_op", "incomplete_op", "disqualified_op", NULL,
+    };
+    static const unsigned char * const flag_legends[] =
+    {
+      "Invisible", "Banned", "Locked", "Incomplete", "Disqualified", NULL,
+    };
+
+    for (int flag = 0; flag_vars[flag]; ++flag) {
+      fprintf(out_f, "<tr><td%s><b>%s</b></td>", cl, flag_legends[flag]);
+      for (int op = 0; flag_op_legends[op]; ++op) {
+        s = "";
+        if (!op) s = " checked=\"checked\"";
+        fprintf(out_f, "<td%s><input type=\"radio\" name=\"%s\" value=\"%d\"%s /> %s</td>",
+                cl, flag_vars[flag], op, s, flag_op_legends[op]);
+      }
+      fprintf(out_f, "</tr>\n");
+    }
+
+    fprintf(out_f, "<tr><td%s><input type=\"submit\" name=\"op_%d\" value=\"%s\" /></td></tr>",
+            cl, SSERV_OP_USER_SEL_CHANGE_REG_FLAGS_PAGE, "Change flags");
+    fprintf(out_f, "</table>\n");
+    fprintf(out_f, "</div>");
+  }
+
+  fprintf(out_f, "</form>\n");
+
+  if (opcaps_check(gcaps, OPCAP_CREATE_USER) >= 0) {
+    fprintf(out_f, "<div id=\"ShowCreateUserMenu\">");
+    fprintf(out_f, "<a onclick=\"setOperationVisibility('CreateUser', true)\">[%s]</a>\n",
+            "User creation operations");
+    fprintf(out_f, "</div>");
+    fprintf(out_f, "<div style=\"display: none;\" id=\"HideCreateUserMenu\">");
+    fprintf(out_f, "<a onclick=\"setOperationVisibility('CreateUser', false)\">[%s]</a><br/>\n",
+            "Hide user creation operations");
+    cl = " class=\"b0\"";
+    fprintf(out_f, "<table%s><tr>", cl);
+    fprintf(out_f, "<td%s>%s[%s]</a></td>", cl,
+            html_hyperref(hbuf, sizeof(hbuf), phr->session_id, phr->self_url,
+                          NULL, "action=%d&amp;op=%d%s%s",
+                          SSERV_CMD_HTTP_REQUEST, SSERV_OP_USER_CREATE_ONE_PAGE,
+                          contest_id_str, group_id_str),
+            "Create one new user");
+    fprintf(out_f, "<td%s>%s[%s]</a></td>", cl,
+            html_hyperref(hbuf, sizeof(hbuf), phr->session_id, phr->self_url,
+                          NULL, "action=%d&amp;op=%d%s%s",
+                          SSERV_CMD_HTTP_REQUEST, SSERV_OP_USER_CREATE_MANY_PAGE,
+                          contest_id_str, group_id_str),
+            "Create MANY new users");
+    fprintf(out_f, "<td%s>%s[%s]</a></td>", cl,
+            html_hyperref(hbuf, sizeof(hbuf), phr->session_id, phr->self_url,
+                          NULL, "action=%d&amp;op=%d%s%s",
+                          SSERV_CMD_HTTP_REQUEST, SSERV_OP_USER_CREATE_FROM_CSV_PAGE,
+                          contest_id_str, group_id_str),
+            "Create users from a CSV table");
+    fprintf(out_f, "</tr></table>\n");
+    fprintf(out_f, "</div>");
+  }
+
+do_footer:
+  ss_write_html_footer(out_f);
+
+cleanup:
+  userlist_free(&users->b); users = 0;
+  xfree(xml_text); xml_text = 0;
+  html_armor_free(&ab);
+  bitset_free(&marked);
+  xfree(marked_str);
+  return retval;
+}
+
+static unsigned char *
+collect_marked_set(
+        struct super_http_request_info *phr,
+        bitset_t *pms)
+{
+  const unsigned char *s = 0;
+
+  if (ss_cgi_param(phr, "marked", &s) > 0 && s) {
+    bitset_url_decode(s, pms);
+  }
+
+  int min_user_id = 0;
+  int max_user_id = 0;
+  ss_cgi_param_int_opt(phr, "min_user_id", &min_user_id, 0);
+  ss_cgi_param_int_opt(phr, "max_user_id", &max_user_id, 0);
+  if (min_user_id <= 0 || min_user_id > EJ_MAX_USER_ID
+      || max_user_id <= 0 || max_user_id > EJ_MAX_USER_ID
+      || min_user_id > max_user_id) return bitset_url_encode(pms);
+
+  bitset_resize(pms, max_user_id + 1);
+  for (int i = min_user_id; i <= max_user_id; ++i)
+    bitset_off(pms, i);
+
+  for (int i = 0; i < phr->param_num; ++i) {
+    if (!strncmp(phr->param_names[i], "user_", 5)) {
+      int user_id = 0, n = 0;
+      if (sscanf(phr->param_names[i] + 5, "%d%n", &user_id, &n) == 1
+          && !phr->param_names[i][n + 5]
+          && user_id >= min_user_id
+          && user_id <= max_user_id) {
+        if (phr->param_sizes[i] == 1 && phr->params[i][0] == '1') {
+          bitset_on(pms, user_id);
+        }
+      }
+    }
+  }
+  return bitset_url_encode(pms);
+}
+
+int
+super_serve_op_USER_FILTER_CHANGE_ACTION(
+        FILE *log_f,
+        FILE *out_f,
+        struct super_http_request_info *phr)
+{
+  int retval = 0;
+  long long total_count = 0;
+  int user_offset = 0;
+  int user_count = 0;
+  int value, r;
+  int contest_id = 0, group_id = 0;
+  const struct contest_desc *cnts = 0;
+  opcap_t gcaps = 0;
+  opcap_t caps = 0;
+  bitset_t marked = BITSET_INITIALIZER;
+  unsigned char *marked_str = 0;
+  int notfirst = 0;
+  FILE *extra_f = 0;
+  char *extra_t = 0;
+  size_t extra_z = 0;
+
+  ss_cgi_param_int_opt(phr, "contest_id", &contest_id, 0);
+  ss_cgi_param_int_opt(phr, "group_id", &group_id, 0);
+  marked_str = collect_marked_set(phr, &marked);
+
+  if (contest_id < 0) contest_id = 0;
+  if (contest_id > 0) {
+    if (contests_get(contest_id, &cnts) < 0 || !cnts) contest_id = 0;
+  }
+  if (group_id < 0) group_id = 0;
+
+  extra_f = open_memstream(&extra_t, &extra_z);
+  if (contest_id > 0) {
+    fprintf(extra_f, "contest_id=%d", contest_id);
+    notfirst = 1;
+  }
+  if (group_id > 0) {
+    if (notfirst) putc('&', extra_f);
+    notfirst = 1;
+    fprintf(extra_f, "group_id=%d", group_id);
+  }
+  if (marked_str && *marked_str) {
+    if (notfirst) putc('&', extra_f);
+    notfirst = 1;
+    fprintf(extra_f, "marked=%s", marked_str);
+  }
+  fclose(extra_f); extra_f = 0;
+
+  if (get_global_caps(phr, &gcaps) >= 0 && opcaps_check(gcaps, OPCAP_LIST_USERS) >= 0) {
+    // this user can view the full user list and the user list for any contest
+  } else if (!cnts) {
+    // user without global OPCAP_LIST_USERS capability cannot view the full user list
+    FAIL(S_ERR_PERM_DENIED);
+  } else if (get_contest_caps(phr, cnts, &caps) < 0 || opcaps_check(caps, OPCAP_LIST_USERS) < 0) {
+    FAIL(S_ERR_PERM_DENIED);
+  }
+
+  if (!phr->userlist_clnt) {
+    goto cleanup;
+  }
+  if ((r = userlist_clnt_get_count(phr->userlist_clnt, ULS_GET_USER_COUNT,
+                                   contest_id, group_id, 0, &total_count)) < 0) {
+    err("set_user_filter: get_count failed: %d", -r);
+    goto cleanup;
+  }
+  if (total_count <= 0) goto cleanup;
+  if (phr->ss->user_filter_set) {
+    user_offset = phr->ss->user_offset;
+    user_count = phr->ss->user_count;
+  }
+  if (user_count <= 0) user_count = 20;
+  if (user_count > 200) user_count = 200;
+
+  switch (phr->opcode) {
+  case SSERV_OP_USER_FILTER_CHANGE_ACTION:
+    if (ss_cgi_param_int(phr, "user_offset", &value) >= 0) {
+      user_offset = value;
+    }
+    if (ss_cgi_param_int(phr, "user_count", &value) >= 0) {
+      user_count = value;
+    }
+    if (user_count <= 0) user_count = 20;
+    if (user_count > 200) user_count = 200;
+    break;
+
+  case SSERV_OP_USER_FILTER_FIRST_PAGE_ACTION:
+    user_offset = 0;
+    break;
+  case SSERV_OP_USER_FILTER_PREV_PAGE_ACTION:
+    user_offset -= user_count;
+    break;
+  case SSERV_OP_USER_FILTER_NEXT_PAGE_ACTION:
+    user_offset += user_count;
+    break;
+  case SSERV_OP_USER_FILTER_LAST_PAGE_ACTION:
+    user_offset = total_count;
+    break;
+  }
+
+  if (user_offset + user_count > total_count) {
+    user_offset = total_count - user_count;
+  }
+  if (user_offset < 0) user_offset = 0;
+  phr->ss->user_filter_set = 1;
+  phr->ss->user_offset = user_offset;
+  phr->ss->user_count = user_count;
+
+cleanup:
+  ss_redirect(out_f, phr, SSERV_OP_USER_BROWSE_PAGE, extra_t);
+  bitset_free(&marked);
+  xfree(marked_str);
+  if (extra_f) fclose(extra_f);
+  xfree(extra_t);
+  return retval;
+}
+
+int
+super_serve_op_USER_JUMP_CONTEST_ACTION(
+        FILE *log_f,
+        FILE *out_f,
+        struct super_http_request_info *phr)
+{
+  int retval = 0;
+  int contest_id = 0, group_id = 0, jump_contest_id = 0;
+  const struct contest_desc *cnts = 0;
+
+  ss_cgi_param_int_opt(phr, "contest_id", &contest_id, 0);
+  ss_cgi_param_int_opt(phr, "group_id", &group_id, 0);
+  ss_cgi_param_int_opt(phr, "jump_contest_id", &jump_contest_id, 0);
+
+  if (jump_contest_id < 0) jump_contest_id = 0;
+  if (jump_contest_id > 0 && (contests_get(jump_contest_id, &cnts) < 0 || !cnts)) {
+    cnts = 0;
+    jump_contest_id = contest_id;
+    if (jump_contest_id > 0 && (contests_get(jump_contest_id, &cnts) < 0 || !cnts)) {
+      jump_contest_id = 0;
+    }
+  }
+
+  ss_redirect_2(out_f, phr, SSERV_OP_USER_BROWSE_PAGE, jump_contest_id, group_id, 0, NULL);
+
+  return retval;
+}
+
+int
+super_serve_op_USER_JUMP_GROUP_ACTION(
+        FILE *log_f,
+        FILE *out_f,
+        struct super_http_request_info *phr)
+{
+  int retval = 0, r;
+  int contest_id = 0, group_id = 0, jump_group_id = 0, real_jump_group_id = 0;
+  unsigned char *xml_text = NULL;
+  struct userlist_list *users = NULL;
+
+  ss_cgi_param_int_opt(phr, "contest_id", &contest_id, 0);
+  ss_cgi_param_int_opt(phr, "group_id", &group_id, 0);
+  ss_cgi_param_int_opt(phr, "jump_group_id", &jump_group_id, 0);
+
+  if (jump_group_id < 0) jump_group_id = 0;
+  if (group_id < 0) group_id = 0;
+
+  if (jump_group_id > 0) {
+    if (!phr->userlist_clnt) FAIL(S_ERR_DB_ERROR);
+    r = userlist_clnt_list_all_users(phr->userlist_clnt, ULS_GET_GROUP_INFO,
+                                     jump_group_id, &xml_text);
+    if (r < 0) FAIL(S_ERR_DB_ERROR);
+    users = userlist_parse_str(xml_text);
+    xfree(xml_text); xml_text = NULL;
+    if (!users) FAIL(S_ERR_DB_ERROR);
+    if (jump_group_id < users->group_map_size && users->group_map[jump_group_id]) {
+      real_jump_group_id = jump_group_id;
+    }
+    userlist_free(&users->b); users = NULL;
+  }
+  if (group_id > 0) {
+    if (!phr->userlist_clnt) FAIL(S_ERR_DB_ERROR);
+    r = userlist_clnt_list_all_users(phr->userlist_clnt, ULS_GET_GROUP_INFO,
+                                     group_id, &xml_text);
+    if (r < 0) FAIL(S_ERR_DB_ERROR);
+    users = userlist_parse_str(xml_text);
+    xfree(xml_text); xml_text = NULL;
+    if (!users) FAIL(S_ERR_DB_ERROR);
+    if (group_id < users->group_map_size && users->group_map[group_id]) {
+      real_jump_group_id = group_id;
+    }
+    userlist_free(&users->b); users = NULL;
+  }
+
+cleanup:
+  ss_redirect_2(out_f, phr, SSERV_OP_USER_BROWSE_PAGE, contest_id, real_jump_group_id, 0, NULL);
+
+  userlist_free(&users->b); users = NULL;
+  xfree(xml_text); xml_text = NULL;
+  return retval;
+}
+
+int
+super_serve_op_USER_BROWSE_MARK_ALL_ACTION(
+        FILE *log_f,
+        FILE *out_f,
+        struct super_http_request_info *phr)
+{
+  int retval = 0, r;
+  int contest_id = 0, group_id = 0;
+  bitset_t marked = BITSET_INITIALIZER;
+  unsigned char *marked_str = 0;
+  const struct contest_desc *cnts = 0;
+  int user_id = 0;
+  struct userlist_list *users = 0;
+  unsigned char *xml_text = 0;
+  int notfirst = 0;
+  FILE *extra_f = 0;
+  char *extra_t = 0;
+  size_t extra_z = 0;
+
+  ss_cgi_param_int_opt(phr, "contest_id", &contest_id, 0);
+  ss_cgi_param_int_opt(phr, "group_id", &group_id, 0);
+  marked_str = collect_marked_set(phr, &marked);
+
+  if (contests_get(contest_id, &cnts) < 0 || !cnts) {
+    contest_id = 0;
+  }
+  if (group_id < 0) group_id = 0;
+
+  if (phr->opcode == SSERV_OP_USER_BROWSE_UNMARK_ALL_ACTION) {
+    xfree(marked_str); marked_str = 0;
+    goto cleanup;
+  }
+
+  // get the IDs of all users registered for contest
+  r = userlist_clnt_list_all_users(phr->userlist_clnt, ULS_LIST_ALL_USERS, cnts->id, &xml_text);
+  if (r < 0 || !xml_text) goto cleanup;
+  users = userlist_parse_str(xml_text);
+  if (!users) goto cleanup;
+  xfree(xml_text); xml_text = 0;
+  bitset_resize(&marked, users->user_map_size);
+
+  if (phr->opcode == SSERV_OP_USER_BROWSE_MARK_ALL_ACTION) {
+    for (user_id = 1; user_id < users->user_map_size; ++user_id) {
+      if (users->user_map[user_id]) {
+        bitset_on(&marked, user_id);
+      }
+    }
+  } else if (phr->opcode == SSERV_OP_USER_BROWSE_TOGGLE_ALL_ACTION) {
+    for (user_id = 1; user_id < users->user_map_size; ++user_id) {
+      if (users->user_map[user_id]) {
+        bitset_toggle(&marked, user_id);
+      }
+    }
+  }
+
+  xfree(marked_str);
+  marked_str = bitset_url_encode(&marked);
+
+cleanup:
+  extra_f = open_memstream(&extra_t, &extra_z);
+  if (contest_id > 0) {
+    fprintf(extra_f, "contest_id=%d", contest_id);
+    notfirst = 1;
+  }
+  if (group_id > 0) {
+    if (notfirst) putc('&', extra_f);
+    notfirst = 1;
+    fprintf(extra_f, "group_id=%d", group_id);
+  }
+  if (marked_str && *marked_str) {
+    if (notfirst) putc('&', extra_f);
+    notfirst = 1;
+    fprintf(extra_f, "marked=%s", marked_str);
+  }
+  fclose(extra_f); extra_f = 0;
+  ss_redirect(out_f, phr, SSERV_OP_USER_BROWSE_PAGE, extra_t);
+  xfree(marked_str);
+  bitset_free(&marked);
+  if (extra_f) fclose(extra_f);
+  xfree(extra_t);
+  userlist_free(&users->b); users = 0;
+  xfree(xml_text);
+  return retval;
+}
+
+/*
+handles: USER_SEL_RANDOM_PASSWD_PAGE
+         USER_SEL_CLEAR_CNTS_PASSWD_PAGE
+         USER_SEL_RANDOM_CNTS_PASSWD_PAGE
+         USER_SEL_DELETE_REG_PAGE
+         USER_SEL_CHANGE_REG_STATUS_PAGE
+         USER_SEL_CHANGE_REG_FLAGS_PAGE
+         USER_SEL_CREATE_REG_PAGE
+         USER_SEL_CREATE_REG_AND_COPY_PAGE
+         USER_SEL_ADD_TO_GROUP_PAGE
+ */
+int
+super_serve_op_USER_SEL_RANDOM_PASSWD_PAGE(
+        FILE *log_f,
+        FILE *out_f,
+        struct super_http_request_info *phr)
+{
+  int retval = 0;
+  int contest_id = 0, group_id = 0;
+  unsigned char contest_id_str[128];
+  unsigned char group_id_str[128];
+  unsigned char buf[1024];
+  unsigned char *marked_str = 0;
+  bitset_t marked = BITSET_INITIALIZER;
+  const struct contest_desc *cnts = 0;
+  int user_id = 0, user_count = 0, serial;
+  const unsigned char *cl = 0;
+  unsigned char *xml_text = 0;
+  struct userlist_list *users = 0;
+  struct userlist_list *groups = 0;
+  const struct userlist_user *u = 0;
+  const struct userlist_contest *reg = 0;
+  const struct userlist_user_info *ui = 0;
+  const struct userlist_group *g = 0;
+  const unsigned char *s = 0;
+  struct html_armor_buffer ab = HTML_ARMOR_INITIALIZER;
+  opcap_t gcaps = 0, caps = 0;
+  int r;
+  int need_privileged = 0, need_invisible = 0, need_banned = 0, need_locked = 0, need_disqualified = 0;
+  int operation = 0;
+  const unsigned char *button_label = 0;
+  int status = USERLIST_REG_REJECTED;
+  int invisible_op = 0, banned_op = 0, locked_op = 0, incomplete_op = 0, disqualified_op = 0;
+  int is_set_changed = 0;
+  const int *cnts_id_list = 0;
+  int cnts_id_count = 0;
+  int other_contest_id = 0;
+  int other_group_id = 0;
+  const struct contest_desc *other_cnts = 0;
+  unsigned char *group_name = NULL;
+  unsigned char *group_desc = NULL;
+
+  ss_cgi_param_int_opt(phr, "contest_id", &contest_id, 0);
+  ss_cgi_param_int_opt(phr, "group_id", &group_id, 0);
+  marked_str = collect_marked_set(phr, &marked);
+
+  if (contest_id < 0) contest_id = 0;
+  if (contest_id > 0) {
+    if (contests_get(contest_id, &cnts) < 0 || !cnts) contest_id = 0;
+  }
+  contest_id_str[0] = 0;
+  if (contest_id > 0) {
+    snprintf(contest_id_str, sizeof(contest_id_str), "&amp;contest_id=%d", contest_id);
+  }
+  if (group_id < 0) group_id = 0;
+  if (group_id > 0) {
+    r = userlist_clnt_list_all_users(phr->userlist_clnt, ULS_GET_GROUP_INFO, group_id, &xml_text);
+    if (r >= 0) {
+      users = userlist_parse_str(xml_text);
+      if (users && group_id < users->group_map_size && (g = users->group_map[group_id])) {
+        group_name = xstrdup(g->group_name);
+        group_desc = xstrdup(g->description);
+      } else {
+        group_id = 0;
+      }
+    } else {
+      group_id = 0;
+    }
+    userlist_free(&users->b); users = 0;
+    xfree(xml_text); xml_text = 0;
+  }
+  group_id_str[0] = 0;
+  if (group_id > 0) {
+    snprintf(group_id_str, sizeof(group_id_str), "&amp;group_id=%d", group_id);
+  }
+
+  /* additional parameters */
+  switch (phr->opcode) {
+  case SSERV_OP_USER_SEL_CHANGE_REG_STATUS_PAGE:
+    ss_cgi_param_int_opt(phr, "status", &status, -1);
+    if (status < 0 || status >= USERLIST_REG_LAST) FAIL(S_ERR_INV_VALUE);
+    break;
+  case SSERV_OP_USER_SEL_CHANGE_REG_FLAGS_PAGE:
+    ss_cgi_param_int_opt(phr, "invisible_op", &invisible_op, 0);
+    ss_cgi_param_int_opt(phr, "banned_op", &banned_op, 0);
+    ss_cgi_param_int_opt(phr, "locked_op", &locked_op, 0);
+    ss_cgi_param_int_opt(phr, "incomplete_op", &incomplete_op, 0);
+    ss_cgi_param_int_opt(phr, "disqualified_op", &disqualified_op, 0);
+    if (invisible_op < 0 || invisible_op > 3) invisible_op = 0;
+    if (banned_op < 0 || banned_op > 3) banned_op = 0;
+    if (locked_op < 0 || locked_op > 3) locked_op = 0;
+    if (incomplete_op < 0 || incomplete_op > 3) incomplete_op = 0;
+    if (disqualified_op < 0 || disqualified_op > 3) disqualified_op = 0;
+    break;
+  case SSERV_OP_USER_SEL_CREATE_REG_PAGE:
+  case SSERV_OP_USER_SEL_CREATE_REG_AND_COPY_PAGE:
+    ss_cgi_param_int_opt(phr, "other_contest_id", &other_contest_id, 0);
+    if (other_contest_id <= 0 || contests_get(other_contest_id, &other_cnts) < 0 || !other_cnts) {
+      other_contest_id = 0;
+    }
+    cnts_id_count = contests_get_list(&cnts_id_list);
+    break;
+  case SSERV_OP_USER_SEL_CREATE_GROUP_MEMBER_PAGE:
+    ss_cgi_param_int_opt(phr, "other_group_id", &other_group_id, 0);
+    if (other_group_id < 0) other_group_id = 0;
+    if (other_group_id > 0) {
+      r = userlist_clnt_list_all_users(phr->userlist_clnt, ULS_GET_GROUP_INFO, other_group_id, &xml_text);
+      if (r < 0) other_group_id = 0;
+      users = userlist_parse_str(xml_text);
+      if (!users || group_id >= users->group_map_size || !users->group_map[other_group_id])
+        other_group_id = 0;
+      userlist_free(&users->b); users = 0;
+      xfree(xml_text); xml_text = 0;
+    }
+    r = userlist_clnt_list_all_users(phr->userlist_clnt, ULS_LIST_ALL_GROUPS, 0, &xml_text);
+    if (r >= 0) {
+      groups = userlist_parse_str(xml_text);
+      xfree(xml_text); xml_text = 0;
+    }
+    break;
+  case SSERV_OP_USER_SEL_DELETE_GROUP_MEMBER_PAGE:
+    if (group_id <= 0) FAIL(S_ERR_INV_VALUE);
+    break;
+  }
+
+  /* contest_id check and preliminary permission check */
+  switch (phr->opcode) {
+  case SSERV_OP_USER_SEL_RANDOM_PASSWD_PAGE:
+    if (get_global_caps(phr, &gcaps) < 0) FAIL(S_ERR_PERM_DENIED);
+    if (opcaps_check(gcaps, OPCAP_EDIT_PASSWD) < 0 && opcaps_check(gcaps, OPCAP_PRIV_EDIT_PASSWD) < 0)
+      FAIL(S_ERR_PERM_DENIED);
+    break;
+  case SSERV_OP_USER_SEL_CLEAR_CNTS_PASSWD_PAGE:
+  case SSERV_OP_USER_SEL_RANDOM_CNTS_PASSWD_PAGE:
+    if (!cnts) FAIL(S_ERR_INV_CONTEST);
+    get_global_caps(phr, &gcaps);
+    get_contest_caps(phr, cnts, &caps);
+    caps |= gcaps;
+    if (opcaps_check(caps, OPCAP_EDIT_PASSWD) < 0 && opcaps_check(caps, OPCAP_PRIV_EDIT_PASSWD) < 0)
+      FAIL(S_ERR_PERM_DENIED);
+    break;
+  case SSERV_OP_USER_SEL_DELETE_REG_PAGE:
+    if (!cnts) FAIL(S_ERR_INV_CONTEST);
+    get_global_caps(phr, &gcaps);
+    if (opcaps_check(gcaps, OPCAP_EDIT_USER) >= 0) gcaps |= 1LL << OPCAP_DELETE_REG;
+    if (opcaps_check(gcaps, OPCAP_PRIV_EDIT_USER) >= 0) gcaps |= 1LL << OPCAP_PRIV_DELETE_REG;
+    get_contest_caps(phr, cnts, &caps);
+    caps |= gcaps;
+    if (opcaps_check(caps, OPCAP_PRIV_DELETE_REG) < 0 && opcaps_check(caps, OPCAP_DELETE_REG) < 0)
+      FAIL(S_ERR_PERM_DENIED);
+    break;
+  case SSERV_OP_USER_SEL_CHANGE_REG_STATUS_PAGE:
+  case SSERV_OP_USER_SEL_CHANGE_REG_FLAGS_PAGE:
+    if (!cnts) FAIL(S_ERR_INV_CONTEST);
+    get_global_caps(phr, &gcaps);
+    if (opcaps_check(gcaps, OPCAP_EDIT_USER) >= 0) gcaps |= 1LL << OPCAP_EDIT_REG;
+    if (opcaps_check(gcaps, OPCAP_PRIV_EDIT_USER) >= 0) gcaps |= 1LL << OPCAP_PRIV_EDIT_REG;
+    get_contest_caps(phr, cnts, &caps);
+    caps |= gcaps;
+    if (opcaps_check(caps, OPCAP_PRIV_EDIT_REG) < 0 && opcaps_check(caps, OPCAP_EDIT_REG) < 0)
+      FAIL(S_ERR_PERM_DENIED);
+    break;
+  case SSERV_OP_USER_SEL_CREATE_REG_PAGE:
+  case SSERV_OP_USER_SEL_CREATE_REG_AND_COPY_PAGE:
+    break;
+  case SSERV_OP_USER_SEL_CREATE_GROUP_MEMBER_PAGE:
+  case SSERV_OP_USER_SEL_DELETE_GROUP_MEMBER_PAGE:
+    if (get_global_caps(phr, &gcaps) < 0) FAIL(S_ERR_PERM_DENIED);
+    if (opcaps_check(gcaps, OPCAP_EDIT_USER) < 0)
+      FAIL(S_ERR_PERM_DENIED);
+    break;
+  default:
+    abort();
+  }
+
+  if (!phr->userlist_clnt) FAIL(S_ERR_DB_ERROR);
+  r = userlist_clnt_list_users_2(phr->userlist_clnt, ULS_LIST_ALL_USERS_3,
+                                 contest_id, group_id, marked_str, 0, 0,
+                                 &xml_text);
+  if (r < 0) FAIL(S_ERR_DB_ERROR);
+  users = userlist_parse_str(xml_text);
+  if (!users) FAIL(S_ERR_DB_ERROR);
+
+  for (user_id = 1; user_id < marked.size; ++user_id) {
+    if (bitset_get(&marked, user_id)) {
+      if (user_id >= users->user_map_size || !(u = users->user_map[user_id])) {
+        bitset_off(&marked, user_id);
+        is_set_changed = 1;
+        continue;
+      }
+      if (contest_id > 0 && !userlist_get_user_contest(u, contest_id)) {
+        bitset_off(&marked, user_id);
+        is_set_changed = 1;
+        continue;
+      }
+      /* per-user check */
+      switch (phr->opcode) {
+      case SSERV_OP_USER_SEL_RANDOM_PASSWD_PAGE:
+        if (is_privileged(phr, cnts, u)) {
+          if (opcaps_check(gcaps, OPCAP_PRIV_EDIT_PASSWD) < 0) u = 0;
+        } else {
+          if (opcaps_check(gcaps, OPCAP_EDIT_PASSWD) < 0) u = 0;
+        }
+        break;
+      case SSERV_OP_USER_SEL_CLEAR_CNTS_PASSWD_PAGE:
+      case SSERV_OP_USER_SEL_RANDOM_CNTS_PASSWD_PAGE:
+        if (is_globally_privileged(phr, u)) {
+          if (opcaps_check(gcaps, OPCAP_PRIV_EDIT_PASSWD) < 0) u = 0;
+        } else if (is_contest_privileged(cnts, u)) {
+          if (opcaps_check(caps, OPCAP_PRIV_EDIT_PASSWD) < 0) u = 0;
+        } else {
+          if (opcaps_check(caps, OPCAP_EDIT_PASSWD) < 0) u = 0;
+        }
+        break;
+      case SSERV_OP_USER_SEL_DELETE_REG_PAGE:
+        if (is_globally_privileged(phr, u)) {
+          if (opcaps_check(gcaps, OPCAP_PRIV_DELETE_REG) < 0) u = 0;
+        } else if (is_contest_privileged(cnts, u)) {
+          if (opcaps_check(caps, OPCAP_PRIV_DELETE_REG) < 0) u = 0;
+        } else {
+          if (opcaps_check(caps, OPCAP_DELETE_REG) < 0) u = 0;
+        }
+        break;
+      case SSERV_OP_USER_SEL_CHANGE_REG_STATUS_PAGE:
+      case SSERV_OP_USER_SEL_CHANGE_REG_FLAGS_PAGE:
+        if (is_globally_privileged(phr, u)) {
+          if (opcaps_check(gcaps, OPCAP_PRIV_EDIT_REG) < 0) u = 0;
+        } else if (is_contest_privileged(cnts, u)) {
+          if (opcaps_check(caps, OPCAP_PRIV_EDIT_REG) < 0) u = 0;
+        } else {
+          if (opcaps_check(caps, OPCAP_EDIT_REG) < 0) u = 0;
+        }
+        break;
+      case SSERV_OP_USER_SEL_CREATE_REG_PAGE:
+      case SSERV_OP_USER_SEL_CREATE_REG_AND_COPY_PAGE:
+        break;
+      case SSERV_OP_USER_SEL_CREATE_GROUP_MEMBER_PAGE:
+      case SSERV_OP_USER_SEL_DELETE_GROUP_MEMBER_PAGE:
+        break;
+      default:
+        abort();
+      }
+      if (!u) {
+        bitset_off(&marked, user_id);
+        is_set_changed = 1;
+        continue;
+      }
+      ++user_count;
+    }
+  }
+  if (user_count <= 0) {
+    ss_redirect_2(out_f, phr, SSERV_OP_USER_BROWSE_PAGE, contest_id, group_id, 0, NULL);
+    goto cleanup;
+  }
+
+  if (is_set_changed) {
+    xfree(marked_str);
+    marked_str = bitset_url_encode(&marked);
+  }
+
+  /* page header generation */
+  switch (phr->opcode) {
+  case SSERV_OP_USER_SEL_RANDOM_PASSWD_PAGE:
+    snprintf(buf, sizeof(buf), "serve-control: %s, generate random registration passwords", phr->html_name);
+    break;
+  case SSERV_OP_USER_SEL_CLEAR_CNTS_PASSWD_PAGE:
+    snprintf(buf, sizeof(buf), "serve-control: %s, clear contest passwords for contest %d",
+             phr->html_name, contest_id);
+    break;
+  case SSERV_OP_USER_SEL_RANDOM_CNTS_PASSWD_PAGE:
+    snprintf(buf, sizeof(buf), "serve-control: %s, generate random contest passwords for contest %d",
+             phr->html_name, contest_id);
+    break;
+  case SSERV_OP_USER_SEL_DELETE_REG_PAGE:
+    snprintf(buf, sizeof(buf), "serve-control: %s, delete registrations from contest %d",
+             phr->html_name, contest_id);
+    break;
+  case SSERV_OP_USER_SEL_CHANGE_REG_STATUS_PAGE:
+    snprintf(buf, sizeof(buf), "serve-control: %s, change registration statuses in contest %d",
+             phr->html_name, contest_id);
+    break;
+  case SSERV_OP_USER_SEL_CHANGE_REG_FLAGS_PAGE:
+    if (!(invisible_op + banned_op + locked_op + incomplete_op + disqualified_op)) {
+      ss_redirect_2(out_f, phr, SSERV_OP_USER_BROWSE_PAGE, contest_id, group_id, 0, marked_str);
+      goto cleanup;
+    }
+    snprintf(buf, sizeof(buf), "serve-control: %s, change registration flags in contest %d",
+             phr->html_name, contest_id);
+    break;
+  case SSERV_OP_USER_SEL_CREATE_REG_PAGE:
+    snprintf(buf, sizeof(buf), "serve-control: %s, register users for another contest",
+             phr->html_name);
+    break;
+  case SSERV_OP_USER_SEL_CREATE_REG_AND_COPY_PAGE:
+    snprintf(buf, sizeof(buf), "serve-control: %s, register users for another contest and copy data",
+             phr->html_name);
+    break;
+  case SSERV_OP_USER_SEL_CREATE_GROUP_MEMBER_PAGE:
+    snprintf(buf, sizeof(buf), "serve-control: %s, add users to a group",
+             phr->html_name);
+    break;
+  case SSERV_OP_USER_SEL_DELETE_GROUP_MEMBER_PAGE:
+    snprintf(buf, sizeof(buf), "serve-control: %s, remove users from group %d",
+             phr->html_name, group_id);
+    break;
+  default:
+    abort();
+  }
+
+  ss_write_html_header(out_f, phr, buf, 0, NULL);
+
+  fprintf(out_f, "<script language=\"javascript\">\n");
+  fprintf(out_f,
+          "function updateCnts1()\n"
+          "{\n"
+          "  var obj1 = document.getElementById(\"cnts1\");\n"
+          "  var obj2 = document.getElementById(\"cnts2\");\n"
+          "  var value = obj1.value;\n"
+          "  var i;\n"
+          "  for (i = 0; i < obj2.options.length; ++i) {\n"
+          "    if (obj2.options[i].value == value) {\n"
+          "      obj2.options.selectedIndex = i;\n"
+          "      break;\n"
+          "    }\n"
+          "  }\n"
+          "}\n");
+  fprintf(out_f,
+          "function updateCnts2()\n"
+          "{\n"
+          "  var obj1 = document.getElementById(\"cnts1\");\n"
+          "  var obj2 = document.getElementById(\"cnts2\");\n"
+          "  var value = obj2.options[obj2.selectedIndex].value;\n"
+          "  obj1.value = value;\n"
+          "}\n");
+  fprintf(out_f,
+          "function updateGroup1()\n"
+          "{\n"
+          "  var obj1 = document.getElementById(\"group1\");\n"
+          "  var obj2 = document.getElementById(\"group2\");\n"
+          "  var value = obj1.value;\n"
+          "  var i;\n"
+          "  for (i = 0; i < obj2.options.length; ++i) {\n"
+          "    if (obj2.options[i].value == value) {\n"
+          "      obj2.options.selectedIndex = i;\n"
+          "      break;\n"
+          "    }\n"
+          "  }\n"
+          "}\n");
+  fprintf(out_f,
+          "function updateGroup2()\n"
+          "{\n"
+          "  var obj1 = document.getElementById(\"group1\");\n"
+          "  var obj2 = document.getElementById(\"group2\");\n"
+          "  var value = obj2.options[obj2.selectedIndex].value;\n"
+          "  obj1.value = value;\n"
+          "}\n");
+  fprintf(out_f, "</script>\n");
+
+  fprintf(out_f, "<h1>%s</h1>\n<br/>\n", buf);
+
+  /* additional info */
+  switch (phr->opcode) {
+  case SSERV_OP_USER_SEL_CLEAR_CNTS_PASSWD_PAGE:
+  case SSERV_OP_USER_SEL_RANDOM_CNTS_PASSWD_PAGE:
+  case SSERV_OP_USER_SEL_DELETE_REG_PAGE:
+  case SSERV_OP_USER_SEL_CHANGE_REG_STATUS_PAGE:
+  case SSERV_OP_USER_SEL_CHANGE_REG_FLAGS_PAGE:
+    fprintf(out_f, "<h2>%s</h2>\n", ARMOR(cnts->name));
+    break;
+  case SSERV_OP_USER_SEL_DELETE_GROUP_MEMBER_PAGE:
+    fprintf(out_f, "<h2>Group: %s</h2>\n", ARMOR(group_name));
+    break;
+  }
+
+  print_top_navigation_links(log_f, out_f, phr, contest_id, group_id, 0, marked_str);
+
+  html_start_form(out_f, 1, phr->self_url, "");
+  html_hidden(out_f, "SID", "%016llx", phr->session_id);
+  html_hidden(out_f, "action", "%d", SSERV_CMD_HTTP_REQUEST);
+  if (contest_id > 0) {
+    html_hidden(out_f, "contest_id", "%d", contest_id);
+  }
+  if (group_id > 0) {
+    html_hidden(out_f, "group_id", "%d", group_id);
+  }
+  if (marked_str && *marked_str) {
+    html_hidden(out_f, "marked", "%s", marked_str);
+  }
+
+  /* additional info */
+  switch (phr->opcode) {
+  case SSERV_OP_USER_SEL_RANDOM_PASSWD_PAGE:
+    fprintf(out_f, "<p>The registration passwords are to be regenerated for the following %d users:</p>\n",
+            user_count);
+    operation = SSERV_OP_USER_SEL_RANDOM_PASSWD_ACTION;
+    button_label = "Generate passwords!";
+    break;
+  case SSERV_OP_USER_SEL_CLEAR_CNTS_PASSWD_PAGE:
+    fprintf(out_f, "<p>The contest passwords are to be cleared for the following %d users:</p>\n",
+            user_count);
+    operation = SSERV_OP_USER_SEL_CLEAR_CNTS_PASSWD_ACTION;
+    button_label = "Clear contest passwords!";
+    break;
+  case SSERV_OP_USER_SEL_RANDOM_CNTS_PASSWD_PAGE:
+    fprintf(out_f, "<p>The contest passwords are to be regenerated for the following %d users:</p>\n",
+            user_count);
+    operation = SSERV_OP_USER_SEL_RANDOM_CNTS_PASSWD_ACTION;
+    button_label = "Generate contest passwords!";
+    break;
+  case SSERV_OP_USER_SEL_DELETE_REG_PAGE:
+    fprintf(out_f, "<p>The registrations are to be deleted for the following %d users:</p>\n", user_count);
+    operation = SSERV_OP_USER_SEL_DELETE_REG_ACTION;
+    button_label = "Delete registrations!";
+    break;
+  case SSERV_OP_USER_SEL_CHANGE_REG_STATUS_PAGE:
+    html_hidden(out_f, "status", "%d", status);
+    fprintf(out_f, "<p>The registration status is to be changed to %s for the following %d users:</p>\n",
+            reg_status_strs[status], user_count);
+    operation = SSERV_OP_USER_SEL_CHANGE_REG_STATUS_ACTION;
+    button_label = "Change!";
+    break;
+  case SSERV_OP_USER_SEL_CHANGE_REG_FLAGS_PAGE:
+    html_hidden(out_f, "invisible_op", "%d", invisible_op);
+    html_hidden(out_f, "banned_op", "%d", banned_op);
+    html_hidden(out_f, "locked_op", "%d", locked_op);
+    html_hidden(out_f, "incomplete_op", "%d", incomplete_op);
+    html_hidden(out_f, "disqualified_op", "%d", disqualified_op);
+    fprintf(out_f, "<p>The registration flags are to be changed for the following %d users as follows:</p>\n",
+            user_count);
+    cl = " class=\"b0\"";
+    fprintf(out_f, "<table%s>", cl);
+    if (invisible_op > 0) {
+      fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>%s</td></tr>",
+              cl, "Invisible", cl, flag_op_legends[invisible_op]);
+    }
+    if (banned_op > 0) {
+      fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>%s</td></tr>",
+              cl, "Banned", cl, flag_op_legends[banned_op]);
+    }
+    if (locked_op > 0) {
+      fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>%s</td></tr>",
+              cl, "Locked", cl, flag_op_legends[locked_op]);
+    }
+    if (incomplete_op > 0) {
+      fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>%s</td></tr>",
+              cl, "Incomplete", cl, flag_op_legends[incomplete_op]);
+    }
+    if (disqualified_op > 0) {
+      fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>%s</td></tr>",
+              cl, "Disqualified", cl, flag_op_legends[disqualified_op]);
+    }
+    fprintf(out_f, "</table>\n");
+    operation = SSERV_OP_USER_SEL_CHANGE_REG_FLAGS_ACTION;
+    button_label = "Change!";
+    break;
+  case SSERV_OP_USER_SEL_CREATE_REG_PAGE:
+  case SSERV_OP_USER_SEL_CREATE_REG_AND_COPY_PAGE:
+    cl = " class=\"b0\"";
+    fprintf(out_f, "<table%s>\n", cl);
+    buf[0] = 0;
+    if (other_contest_id > 0) snprintf(buf, sizeof(buf), "%d", other_contest_id);
+    fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s><input id=\"cnts1\" onchange=\"updateCnts1()\" type=\"text\" name=\"other_contest_id_1\" size=\"20\" value=\"%s\"/></td></tr>\n",
+            cl, "Contest ID", cl, buf);
+    fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>", cl, "Contest name", cl);
+    fprintf(out_f, "<select id=\"cnts2\" onchange=\"updateCnts2()\" name=\"other_contest_id_2\"><option value=\"0\"></option>");
+    for (int i = 0; i < cnts_id_count; ++i) {
+      int other_contest_id_2 = cnts_id_list[i];
+      if (other_contest_id_2 <= 0) continue;
+      if (contests_get(other_contest_id_2, &cnts) < 0 || !cnts) continue;
+      if (cnts->closed) continue;
+      s = "";
+      if (other_contest_id_2 == other_contest_id) s = " selected=\"selected\"";
+      fprintf(out_f, "<option value=\"%d\"%s>%s</option>", other_contest_id_2, s, ARMOR(cnts->name));
+    }
+    fprintf(out_f, "</select>");
+    fprintf(out_f, "</td></tr>\n");
+    fprintf(out_f, "</table>\n");
+
+    switch (phr->opcode) {
+    case SSERV_OP_USER_SEL_CREATE_REG_PAGE:
+      operation = SSERV_OP_USER_SEL_CREATE_REG_ACTION;
+      button_label = "Register!";
+      break;
+    case SSERV_OP_USER_SEL_CREATE_REG_AND_COPY_PAGE:
+      operation = SSERV_OP_USER_SEL_CREATE_REG_AND_COPY_ACTION;
+      button_label = "Register and copy!";
+      break;
+    }
+    break;
+  case SSERV_OP_USER_SEL_CREATE_GROUP_MEMBER_PAGE:
+    cl = " class=\"b0\"";
+    fprintf(out_f, "<table%s>\n", cl);
+    buf[0] = 0;
+    if (other_group_id > 0) snprintf(buf, sizeof(buf), "%d", other_group_id);
+    fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s><input id=\"group1\" onchange=\"updateGroup1()\" type=\"text\" name=\"other_group_id_1\" size=\"20\" value=\"%s\"/></td></tr>\n",
+            cl, "Group ID", cl, buf);
+    fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>", cl, "Group name", cl);
+    fprintf(out_f, "<select id=\"group2\" onchange=\"updateGroup2()\" name=\"other_group_id_2\"><option value=\"0\"></option>");
+    if (groups) {
+      for (int i = 0; i < groups->group_map_size; ++i) {
+        if (!(g = groups->group_map[i])) continue;
+        s = "";
+        if (i == other_group_id) s = " selected=\"selected\"";
+        fprintf(out_f, "<option value=\"%d\"%s>%s</option>", i, s, ARMOR(g->group_name));
+      }
+    }
+    fprintf(out_f, "</select>");
+    fprintf(out_f, "</td></tr>\n");
+    fprintf(out_f, "</table>\n");
+    operation = SSERV_OP_USER_SEL_CREATE_GROUP_MEMBER_ACTION;
+    button_label = "Add to group";
+    break;
+  case SSERV_OP_USER_SEL_DELETE_GROUP_MEMBER_PAGE:
+    fprintf(out_f, "<p>The following users are to be removed from group %d:</p>\n", group_id);
+    operation = SSERV_OP_USER_SEL_DELETE_GROUP_MEMBER_ACTION;
+    button_label = "Remove from group";
+    break;
+  default:
+    abort();
+  }
+
+  cl = " class=\"b1\"";
+  fprintf(out_f, "<table%s>", cl);
+  fprintf(out_f, "<tr><th%s>%s</th><th%s>%s</th><th%s>%s</th><th%s>%s</th><th%s>%s</th><th%s>%s</th></tr>\n",
+          cl, "NN", cl, "User ID", cl, "Login", cl, "Name", cl, "Status", cl, "Flags");
+  for (user_id = 1, serial = 0; user_id < marked.size; ++user_id) {
+    if (bitset_get(&marked, user_id)) {
+      u = users->user_map[user_id];
+      ui = u->cnts0;
+      reg = 0;
+      if (contest_id > 0) {
+        reg = userlist_get_user_contest(u, contest_id);
+      }
+      fprintf(out_f, "<tr><td%s>%d</td>", cl, ++serial);
+      fprintf(out_f, "<td%s>%d</td>", cl, user_id);
+      fprintf(out_f, "<td%s>%s</td>", cl, ARMOR(u->login));
+      s = u->login;
+      if (ui && ui->name && *ui->name) s = ui->name;
+      fprintf(out_f, "<td%s>%s</td>", cl, ARMOR(s));
+      if (cnts && reg) {
+        r = reg->status;
+        if (r < 0 || r >= USERLIST_REG_LAST) r = USERLIST_REG_LAST;
+        fprintf(out_f, "<td%s>%s</td>", cl, reg_status_strs[r]);
+      } else {
+        fprintf(out_f, "<td%s>&nbsp;</td>", cl);
+      }
+      fprintf(out_f, "<td%s>", cl);
+      s = "";
+      if (is_privileged(phr, cnts, u)) {
+        need_privileged = 1;
+        fprintf(out_f, "%s%s", s, "privileged");
+        s = ", ";
+      }
+      if (cnts && reg) {
+        if ((reg->flags & USERLIST_UC_INVISIBLE)) {
+          need_invisible = 1;
+          fprintf(out_f, "%s%s", s, "invisible");
+          s = ", ";
+        }
+        if ((reg->flags & USERLIST_UC_BANNED)) {
+          need_banned = 1;
+          fprintf(out_f, "%s%s", s, "banned");
+          s = ", ";
+        }
+        if ((reg->flags & USERLIST_UC_LOCKED)) {
+          need_locked = 1;
+          fprintf(out_f, "%s%s", s, "locked");
+          s = ", ";
+        }
+        if ((reg->flags & USERLIST_UC_DISQUALIFIED)) {
+          need_disqualified = 1;
+          fprintf(out_f, "%s%s", s, "disqualified");
+          s = ", ";
+        }
+      }
+      if (!*s) fprintf(out_f, "&nbsp;");
+      fprintf(out_f, "</td>");
+      fprintf(out_f, "</tr>\n");
+    }
+  }
+  fprintf(out_f, "</table>\n");
+
+  if (phr->opcode != SSERV_OP_USER_SEL_CHANGE_REG_FLAGS_PAGE) {
+    cl = " class=\"b0\"";
+    fprintf(out_f, "<table%s>", cl);
+    if (need_privileged) {
+      fprintf(out_f, "<tr><td%s><input type=\"checkbox\" name=\"%s\" value=\"1\" /></td><td%s>%s</td></tr>\n",
+              cl, "include_privileged", cl, "Peform the operation even for PRIVILEGED users");
+    }
+    if (need_invisible) {
+      fprintf(out_f, "<tr><td%s><input type=\"checkbox\" name=\"%s\" value=\"1\" /></td><td%s>%s</td></tr>\n",
+              cl, "include_invisible", cl, "Peform the operation even for INVISIBLE users");
+    }
+    if (need_banned) {
+      fprintf(out_f, "<tr><td%s><input type=\"checkbox\" name=\"%s\" value=\"1\" /></td><td%s>%s</td></tr>\n",
+              cl, "include_banned", cl, "Peform the operation even for BANNED users");
+    }
+    if (need_locked) {
+      fprintf(out_f, "<tr><td%s><input type=\"checkbox\" name=\"%s\" value=\"1\" /></td><td%s>%s</td></tr>\n",
+              cl, "include_locked", cl, "Peform the operation even for LOCKED users");
+    }
+    if (need_disqualified) {
+      fprintf(out_f, "<tr><td%s><input type=\"checkbox\" name=\"%s\" value=\"1\" /></td><td%s>%s</td></tr>\n",
+              cl, "include_disqualified", cl, "Peform the operation even for DISQUALIFIED users");
+    }
+    fprintf(out_f, "</table>");
+  }
+
+  cl = " class=\"b0\"";
+  fprintf(out_f, "<table%s><tr>", cl);
+  fprintf(out_f, "<tr><td%s><input type=\"submit\" name=\"op_%d\" value=\"%s\" /></td>",
+            cl, SSERV_OP_USER_SEL_CANCEL_ACTION, "Cancel");
+  fprintf(out_f, "<td%s><input type=\"submit\" name=\"op_%d\" value=\"%s\" /></td></tr>",
+            cl, operation, button_label);
+  fprintf(out_f, "</tr></table>\n");
+    
+  fprintf(out_f, "</form>\n");
+
+  ss_write_html_footer(out_f);
+
+cleanup:
+  html_armor_free(&ab);
+  userlist_free(&groups->b); groups = 0;
+  userlist_free(&users->b); users = 0;
+  bitset_free(&marked);
+  xfree(marked_str);
+  xfree(xml_text);
+  xfree(group_name);
+  xfree(group_desc);
+  return retval;
+}
+
+int
+super_serve_op_USER_SEL_RANDOM_PASSWD_ACTION(
+        FILE *log_f,
+        FILE *out_f,
+        struct super_http_request_info *phr)
+{
+  int retval = 0, r;
+  int contest_id = 0, group_id = 0;
+  bitset_t marked = BITSET_INITIALIZER;
+  unsigned char *marked_str = 0;
+  const struct contest_desc *cnts = 0, *other_cnts = 0;
+  int status = -1;
+  int invisible_op = 0, banned_op = 0, locked_op = 0, incomplete_op = 0, disqualified_op = 0;
+  int clear_mask = 0, set_mask = 0, toggle_mask = 0;
+  int other_contest_id = 0;
+  int other_group_id = 0;
+  opcap_t gcaps = 0, caps = 0, rcaps = 0;
+  unsigned char *xml_text = 0;
+  struct userlist_list *users = 0;
+  int user_id = 0, user_count = 0;
+  const struct userlist_user *u = 0;
+  const struct userlist_contest *reg = 0;
+  int include_privileged = 0, include_invisible = 0, include_banned = 0, include_locked = 0, include_disqualified = 0;
+
+  ss_cgi_param_int_opt(phr, "contest_id", &contest_id, 0);
+  ss_cgi_param_int_opt(phr, "group_id", &group_id, 0);
+  marked_str = collect_marked_set(phr, &marked);
+
+  if (contest_id < 0) contest_id = 0;
+  if (contest_id > 0) {
+    if (contests_get(contest_id, &cnts) < 0 || !cnts) contest_id = 0;
+  }
+  if (group_id < 0) group_id = 0;
+  if (group_id > 0) {
+    r = userlist_clnt_list_all_users(phr->userlist_clnt, ULS_GET_GROUP_INFO, group_id, &xml_text);
+    if (r >= 0) {
+      users = userlist_parse_str(xml_text);
+      if (users && group_id < users->group_map_size && users->group_map[group_id]) {
+        // ...
+      } else {
+        group_id = 0;
+      }
+    } else {
+      group_id = 0;
+    }
+    userlist_free(&users->b); users = 0;
+    xfree(xml_text); xml_text = 0;
+  }
+
+  ss_cgi_param_int_opt(phr, "include_privileged", &include_privileged, 0);
+  if (include_privileged != 1) include_privileged = 0;
+  ss_cgi_param_int_opt(phr, "include_invisible", &include_invisible, 0);
+  if (include_invisible != 1) include_invisible = 0;
+  ss_cgi_param_int_opt(phr, "include_banned", &include_banned, 0);
+  if (include_banned != 1) include_banned = 0;
+  ss_cgi_param_int_opt(phr, "include_locked", &include_locked, 0);
+  if (include_locked != 1) include_locked = 0;
+  ss_cgi_param_int_opt(phr, "include_disqualified", &include_disqualified, 0);
+  if (include_disqualified != 1) include_disqualified = 0;
+
+  /* additional parameters */
+  switch (phr->opcode) {
+  case SSERV_OP_USER_SEL_CHANGE_REG_STATUS_ACTION:
+    ss_cgi_param_int_opt(phr, "status", &status, -1);
+    if (status < 0 || status >= USERLIST_REG_LAST) FAIL(S_ERR_INV_VALUE);
+    break;
+  case SSERV_OP_USER_SEL_CHANGE_REG_FLAGS_ACTION:
+    ss_cgi_param_int_opt(phr, "invisible_op", &invisible_op, 0);
+    ss_cgi_param_int_opt(phr, "banned_op", &banned_op, 0);
+    ss_cgi_param_int_opt(phr, "locked_op", &locked_op, 0);
+    ss_cgi_param_int_opt(phr, "incomplete_op", &incomplete_op, 0);
+    ss_cgi_param_int_opt(phr, "disqualified_op", &disqualified_op, 0);
+    if (invisible_op < 0 || invisible_op > 3) invisible_op = 0;
+    if (banned_op < 0 || banned_op > 3) banned_op = 0;
+    if (locked_op < 0 || locked_op > 3) locked_op = 0;
+    if (incomplete_op < 0 || incomplete_op > 3) incomplete_op = 0;
+    if (disqualified_op < 0 || disqualified_op > 3) disqualified_op = 0;
+    if (invisible_op == 1) {
+      clear_mask |= USERLIST_UC_INVISIBLE;
+    } else if (invisible_op == 2) {
+      set_mask |= USERLIST_UC_INVISIBLE;
+    } else if (invisible_op == 3) {
+      toggle_mask |= USERLIST_UC_INVISIBLE;
+    }
+    if (banned_op == 1) {
+      clear_mask |= USERLIST_UC_BANNED;
+    } else if (banned_op == 2) {
+      set_mask |= USERLIST_UC_BANNED;
+    } else if (banned_op == 3) {
+      toggle_mask |= USERLIST_UC_BANNED;
+    }
+    if (locked_op == 1) {
+      clear_mask |= USERLIST_UC_LOCKED;
+    } else if (locked_op == 2) {
+      set_mask |= USERLIST_UC_LOCKED;
+    } else if (locked_op == 3) {
+      toggle_mask |= USERLIST_UC_LOCKED;
+    }
+    if (incomplete_op == 1) {
+      clear_mask |= USERLIST_UC_INCOMPLETE;
+    } else if (incomplete_op == 2) {
+      set_mask |= USERLIST_UC_INCOMPLETE;
+    } else if (incomplete_op == 3) {
+      toggle_mask |= USERLIST_UC_INCOMPLETE;
+    }
+    if (disqualified_op == 1) {
+      clear_mask |= USERLIST_UC_DISQUALIFIED;
+    } else if (disqualified_op == 2) {
+      set_mask |= USERLIST_UC_DISQUALIFIED;
+    } else if (disqualified_op == 3) {
+      toggle_mask |= USERLIST_UC_DISQUALIFIED;
+    }
+    if (!(clear_mask + set_mask + toggle_mask)) goto done;
+    break;
+  case SSERV_OP_USER_SEL_CREATE_REG_ACTION:
+  case SSERV_OP_USER_SEL_CREATE_REG_AND_COPY_ACTION:
+    ss_cgi_param_int_opt(phr, "other_contest_id_1", &other_contest_id, 0);
+    if (other_contest_id <= 0 || contests_get(other_contest_id, &other_cnts) < 0 || !other_cnts) {
+      other_contest_id = 0;
+    }
+    if (!other_cnts) FAIL(S_ERR_INV_CONTEST);
+    break;
+  case SSERV_OP_USER_SEL_CREATE_GROUP_MEMBER_ACTION:
+    ss_cgi_param_int_opt(phr, "other_group_id_1", &other_group_id, 0);
+    if (other_group_id <= 0) FAIL(S_ERR_INV_GROUP_ID);
+    r = userlist_clnt_list_all_users(phr->userlist_clnt, ULS_GET_GROUP_INFO, other_group_id, &xml_text);
+    if (r < 0) FAIL(S_ERR_INV_GROUP_ID);
+    users = userlist_parse_str(xml_text);
+    if (!users || other_group_id >= users->group_map_size || !users->group_map[other_group_id])
+      FAIL(S_ERR_INV_GROUP_ID);
+    userlist_free(&users->b); users = 0;
+    xfree(xml_text); xml_text = 0;
+    break;
+  case SSERV_OP_USER_SEL_DELETE_GROUP_MEMBER_ACTION:
+    if (group_id <= 0) FAIL(S_ERR_INV_GROUP_ID);
+    break;
+  }
+
+  switch (phr->opcode) {
+  case SSERV_OP_USER_SEL_RANDOM_PASSWD_ACTION:
+    if (get_global_caps(phr, &gcaps) < 0) FAIL(S_ERR_PERM_DENIED);
+    if (opcaps_check(gcaps, OPCAP_EDIT_PASSWD) < 0 && opcaps_check(gcaps, OPCAP_PRIV_EDIT_PASSWD) < 0)
+      FAIL(S_ERR_PERM_DENIED);
+    break;
+  case SSERV_OP_USER_SEL_CLEAR_CNTS_PASSWD_ACTION:
+  case SSERV_OP_USER_SEL_RANDOM_CNTS_PASSWD_ACTION:
+    if (!cnts) FAIL(S_ERR_INV_CONTEST);
+    get_global_caps(phr, &gcaps);
+    get_contest_caps(phr, cnts, &caps);
+    caps |= gcaps;
+    if (opcaps_check(caps, OPCAP_EDIT_PASSWD) < 0 && opcaps_check(caps, OPCAP_PRIV_EDIT_PASSWD) < 0)
+      FAIL(S_ERR_PERM_DENIED);
+    break;
+  case SSERV_OP_USER_SEL_DELETE_REG_ACTION:
+    if (!cnts) FAIL(S_ERR_INV_CONTEST);
+    get_global_caps(phr, &gcaps);
+    if (opcaps_check(gcaps, OPCAP_EDIT_USER) >= 0) gcaps |= 1LL << OPCAP_DELETE_REG;
+    if (opcaps_check(gcaps, OPCAP_PRIV_EDIT_USER) >= 0) gcaps |= 1LL << OPCAP_PRIV_DELETE_REG;
+    get_contest_caps(phr, cnts, &caps);
+    caps |= gcaps;
+    if (opcaps_check(caps, OPCAP_PRIV_DELETE_REG) < 0 && opcaps_check(caps, OPCAP_DELETE_REG) < 0)
+      FAIL(S_ERR_PERM_DENIED);
+    break;
+  case SSERV_OP_USER_SEL_CHANGE_REG_STATUS_ACTION:
+  case SSERV_OP_USER_SEL_CHANGE_REG_FLAGS_ACTION:
+    if (!cnts) FAIL(S_ERR_INV_CONTEST);
+    get_global_caps(phr, &gcaps);
+    if (opcaps_check(gcaps, OPCAP_EDIT_USER) >= 0) gcaps |= 1LL << OPCAP_EDIT_REG;
+    if (opcaps_check(gcaps, OPCAP_PRIV_EDIT_USER) >= 0) gcaps |= 1LL << OPCAP_PRIV_EDIT_REG;
+    get_contest_caps(phr, cnts, &caps);
+    caps |= gcaps;
+    if (opcaps_check(caps, OPCAP_PRIV_EDIT_REG) < 0 && opcaps_check(caps, OPCAP_EDIT_REG) < 0)
+      FAIL(S_ERR_PERM_DENIED);
+    break;
+  case SSERV_OP_USER_SEL_CREATE_REG_ACTION:
+    get_global_caps(phr, &gcaps);
+    get_contest_caps(phr, other_cnts, &caps);
+    caps |= gcaps;
+    if (opcaps_check(caps, OPCAP_PRIV_CREATE_REG) < 0 && opcaps_check(caps, OPCAP_CREATE_REG) < 0)
+      FAIL(S_ERR_PERM_DENIED);
+    break;
+  case SSERV_OP_USER_SEL_CREATE_REG_AND_COPY_ACTION:
+    if  (!cnts) FAIL(S_ERR_INV_CONTEST);
+    get_global_caps(phr, &gcaps);
+    get_contest_caps(phr, other_cnts, &caps);
+    caps |= gcaps;
+    if (opcaps_check(caps, OPCAP_PRIV_CREATE_REG) < 0 && opcaps_check(caps, OPCAP_CREATE_REG) < 0)
+      FAIL(S_ERR_PERM_DENIED);
+    get_contest_caps(phr, cnts, &rcaps);
+    rcaps |= gcaps;
+    if (opcaps_check(rcaps, OPCAP_GET_USER) < 0)
+      FAIL(S_ERR_PERM_DENIED);
+    break;
+  case SSERV_OP_USER_SEL_CREATE_GROUP_MEMBER_ACTION:
+  case SSERV_OP_USER_SEL_DELETE_GROUP_MEMBER_ACTION:
+    if (get_global_caps(phr, &gcaps) < 0) FAIL(S_ERR_PERM_DENIED);
+    if (opcaps_check(gcaps, OPCAP_EDIT_USER) < 0)
+      FAIL(S_ERR_PERM_DENIED);
+    break;
+  default:
+    abort();
+  }
+
+  if (!phr->userlist_clnt) FAIL(S_ERR_DB_ERROR);
+  r = userlist_clnt_list_users_2(phr->userlist_clnt, ULS_LIST_ALL_USERS_3,
+                                 contest_id, group_id, marked_str, 0, 0,
+                                 &xml_text);
+  if (r < 0) FAIL(S_ERR_DB_ERROR);
+  users = userlist_parse_str(xml_text);
+  if (!users) FAIL(S_ERR_DB_ERROR);
+
+  for (user_id = 1; user_id < marked.size; ++user_id) {
+    if (bitset_get(&marked, user_id)) {
+      if (user_id >= users->user_map_size || !(u = users->user_map[user_id])) {
+        bitset_off(&marked, user_id);
+        continue;
+      }
+      if (contest_id > 0 && !userlist_get_user_contest(u, contest_id)) {
+        bitset_off(&marked, user_id);
+        continue;
+      }
+      if (!include_privileged && is_privileged(phr, cnts, u)) {
+        bitset_off(&marked, user_id);
+        continue;
+      }
+      if (cnts && phr->opcode != SSERV_OP_USER_SEL_CHANGE_REG_FLAGS_ACTION
+          && (reg = userlist_get_user_contest(u, contest_id))) {
+        if (((reg->flags & USERLIST_UC_INVISIBLE) && !include_invisible)
+            || ((reg->flags & USERLIST_UC_BANNED) && !include_banned)
+            || ((reg->flags & USERLIST_UC_LOCKED) && !include_locked)
+            || ((reg->flags & USERLIST_UC_DISQUALIFIED) && !include_disqualified)) {
+          bitset_off(&marked, user_id);
+          continue;
+        }
+      }
+      switch (phr->opcode) {
+      case SSERV_OP_USER_SEL_RANDOM_PASSWD_ACTION:
+        if (is_privileged(phr, cnts, u)) {
+          if (opcaps_check(gcaps, OPCAP_PRIV_EDIT_PASSWD) < 0) u = 0;
+        } else {
+          if (opcaps_check(gcaps, OPCAP_EDIT_PASSWD) < 0) u = 0;
+        }
+        break;
+      case SSERV_OP_USER_SEL_CLEAR_CNTS_PASSWD_ACTION:
+      case SSERV_OP_USER_SEL_RANDOM_CNTS_PASSWD_ACTION:
+        if (is_globally_privileged(phr, u)) {
+          if (opcaps_check(gcaps, OPCAP_PRIV_EDIT_PASSWD) < 0) u = 0;
+        } else if (is_contest_privileged(cnts, u)) {
+          if (opcaps_check(caps, OPCAP_PRIV_EDIT_PASSWD) < 0) u = 0;
+        } else {
+          if (opcaps_check(caps, OPCAP_EDIT_PASSWD) < 0) u = 0;
+        }
+        break;
+      case SSERV_OP_USER_SEL_DELETE_REG_ACTION:
+        if (is_globally_privileged(phr, u)) {
+          if (opcaps_check(gcaps, OPCAP_PRIV_DELETE_REG) < 0) u = 0;
+        } else if (is_contest_privileged(cnts, u)) {
+          if (opcaps_check(caps, OPCAP_PRIV_DELETE_REG) < 0) u = 0;
+        } else {
+          if (opcaps_check(caps, OPCAP_DELETE_REG) < 0) u = 0;
+        }
+        break;
+      case SSERV_OP_USER_SEL_CHANGE_REG_STATUS_ACTION:
+      case SSERV_OP_USER_SEL_CHANGE_REG_FLAGS_ACTION:
+        if (is_globally_privileged(phr, u)) {
+          if (opcaps_check(gcaps, OPCAP_PRIV_EDIT_REG) < 0) u = 0;
+        } else if (is_contest_privileged(cnts, u)) {
+          if (opcaps_check(caps, OPCAP_PRIV_EDIT_REG) < 0) u = 0;
+        } else {
+          if (opcaps_check(caps, OPCAP_EDIT_REG) < 0) u = 0;
+        }
+        break;
+      case SSERV_OP_USER_SEL_CREATE_REG_ACTION:
+      case SSERV_OP_USER_SEL_CREATE_REG_AND_COPY_ACTION:
+        if (is_globally_privileged(phr, u)) {
+          if (opcaps_check(gcaps, OPCAP_PRIV_CREATE_REG) < 0) u = 0;
+        } else {
+          if (opcaps_check(caps, OPCAP_CREATE_USER) < 0) u = 0;
+        }
+        break;
+      case SSERV_OP_USER_SEL_CREATE_GROUP_MEMBER_ACTION:
+      case SSERV_OP_USER_SEL_DELETE_GROUP_MEMBER_ACTION:
+        break;
+      default:
+        abort();
+      }
+      if (!u) {
+        bitset_off(&marked, user_id);
+        continue;
+      }
+      ++user_count;
+    }
+  }
+  if (user_count <= 0) goto done;
+
+  /* do the requested operation */
+  for (user_id = 1; user_id < marked.size; ++user_id) {
+    if (!bitset_get(&marked, user_id)) continue;
+
+    r = 0;
+    switch (phr->opcode) {
+    case SSERV_OP_USER_SEL_RANDOM_PASSWD_ACTION:
+      r = userlist_clnt_register_contest(phr->userlist_clnt, ULS_RANDOM_PASSWD, user_id,
+                                         contest_id, 0, 0);
+      break;
+    case SSERV_OP_USER_SEL_CLEAR_CNTS_PASSWD_ACTION:
+      r = userlist_clnt_delete_field(phr->userlist_clnt, ULS_DELETE_FIELD,
+                                     user_id, contest_id, 0, USERLIST_NC_TEAM_PASSWD);
+      break;
+    case SSERV_OP_USER_SEL_RANDOM_CNTS_PASSWD_ACTION:
+      r = userlist_clnt_register_contest(phr->userlist_clnt, ULS_RANDOM_TEAM_PASSWD, user_id,
+                                         contest_id, 0, 0);
+      break;
+    case SSERV_OP_USER_SEL_DELETE_REG_ACTION:
+      r = userlist_clnt_change_registration(phr->userlist_clnt, user_id, contest_id, -2, 0, 0);
+      break;
+    case SSERV_OP_USER_SEL_CHANGE_REG_STATUS_ACTION:
+      r = userlist_clnt_change_registration(phr->userlist_clnt, user_id, contest_id, status, 0, 0);
+      break;
+    case SSERV_OP_USER_SEL_CHANGE_REG_FLAGS_ACTION:
+      if (clear_mask > 0 && r >= 0) {
+        r = userlist_clnt_change_registration(phr->userlist_clnt, user_id, contest_id, -1, 2, clear_mask);
+      }
+      if (set_mask > 0 && r >= 0) {
+        r = userlist_clnt_change_registration(phr->userlist_clnt, user_id, contest_id, -1, 1, set_mask);
+      }
+      if (toggle_mask > 0 && r >= 0) {
+        r = userlist_clnt_change_registration(phr->userlist_clnt, user_id, contest_id, -1, 3, toggle_mask);
+      }
+      break;
+    case SSERV_OP_USER_SEL_CREATE_REG_ACTION:
+      r = userlist_clnt_register_contest(phr->userlist_clnt, ULS_PRIV_REGISTER_CONTEST,
+                                         user_id, other_contest_id, 0, 0);
+      break;
+    case SSERV_OP_USER_SEL_CREATE_REG_AND_COPY_ACTION:
+      r = userlist_clnt_register_contest(phr->userlist_clnt, ULS_PRIV_REGISTER_CONTEST,
+                                         user_id, other_contest_id, 0, 0);
+      if (r >= 0) {
+        r = userlist_clnt_copy_user_info(phr->userlist_clnt, user_id, contest_id, other_contest_id);
+      }
+      break;
+    case SSERV_OP_USER_SEL_CREATE_GROUP_MEMBER_ACTION:
+      r = userlist_clnt_register_contest(phr->userlist_clnt, ULS_CREATE_GROUP_MEMBER,
+                                         user_id, other_group_id, 0, 0);
+      break;
+    case SSERV_OP_USER_SEL_DELETE_GROUP_MEMBER_ACTION:
+      r = userlist_clnt_register_contest(phr->userlist_clnt, ULS_DELETE_GROUP_MEMBER,
+                                         user_id, group_id, 0, 0);
+      break;
+    default:
+      abort();
+    }
+    if (r < 0) FAIL(S_ERR_DB_ERROR);
+  }
+
+done:
+  ss_redirect_2(out_f, phr, SSERV_OP_USER_BROWSE_PAGE, contest_id, group_id, 0, NULL);
+
+cleanup:
+  userlist_free(&users->b); users = 0;
+  bitset_free(&marked);
+  xfree(marked_str);
+  xfree(xml_text);
+  return retval;
+}
+
+int
+super_serve_op_USER_SEL_CANCEL_ACTION(
+        FILE *log_f,
+        FILE *out_f,
+        struct super_http_request_info *phr)
+{
+  int contest_id = 0, group_id = 0;
+  bitset_t marked = BITSET_INITIALIZER;
+  unsigned char *marked_str = 0;
+
+  ss_cgi_param_int_opt(phr, "contest_id", &contest_id, 0);
+  ss_cgi_param_int_opt(phr, "group_id", &group_id, 0);
+  marked_str = collect_marked_set(phr, &marked);
+  ss_redirect_2(out_f, phr, SSERV_OP_USER_BROWSE_PAGE, contest_id, group_id, 0, marked_str);
+
+  xfree(marked_str);
+  bitset_free(&marked);
+  return 0;
+}
+
+struct user_row_info
+{
+  int field_id;
+  unsigned char *field_desc;
+};
+
+static char const * const member_string[] =
+{
+  "Contestant",
+  "Reserve",
+  "Coach",
+  "Advisor",
+  "Guest"
+};
+static char const * const member_string_pl[] =
+{
+  "Contestants",
+  "Reserves",
+  "Coaches",
+  "Advisors",
+  "Guests"
+};
+
+static void
+string_row(
+        FILE *out_f,
+        const unsigned char *tr_class,
+        int is_hidden,
+        const unsigned char *td_class,
+        const unsigned char *legend,
+        const unsigned char *param_suffix,
+        const unsigned char *str)
+{
+  unsigned char trcl[256];
+  unsigned char tdcl[256];
+  unsigned char param_name[256];
+  unsigned char buf[1024];
+  unsigned char onchange[1024];
+  const unsigned char *checked = "";
+  const unsigned char *display = "";
+  struct html_armor_buffer ab = HTML_ARMOR_INITIALIZER;
+
+  trcl[0] = 0;
+  if (tr_class) {
+    snprintf(trcl, sizeof(trcl), " class=\"%s\"", tr_class);
+  }
+  tdcl[0] = 0;
+  if (td_class) {
+    snprintf(tdcl, sizeof(tdcl), " class=\"%s\"", td_class);
+  }
+  if (!str) {
+    checked = " checked=\"checked\"";
+    str = "";
+  }
+  snprintf(param_name, sizeof(param_name), "field_%s", param_suffix);
+  if (is_hidden) {
+    display = " style=\"display: none;\"";
+  }
+
+  fprintf(out_f, "<tr%s%s>", trcl, display);
+  fprintf(out_f, "<td%s><b>%s:</b></td>", tdcl, legend);
+  fprintf(out_f, "<td%s><input type=\"checkbox\" onchange=\"checkNull('%s')\" name=\"field_null_%s\" value=\"1\"%s /></td>",
+          tdcl, param_suffix, param_suffix, checked);
+  snprintf(onchange, sizeof(onchange), "uncheckNull('%s')", param_suffix);
+  fprintf(out_f, "<td%s>%s</td>", tdcl,
+          html_input_text_js(buf, sizeof(buf), param_name, 50, onchange, "%s", ARMOR(str)));
+  fprintf(out_f, "<td%s>&nbsp;</td>", tdcl);
+  fprintf(out_f, "</tr>\n");
+  html_armor_free(&ab);
+}
+
+static const struct user_row_info user_flag_rows[] =
+{
+  { USERLIST_NN_IS_PRIVILEGED, "Globally privileged" },
+  { USERLIST_NN_IS_INVISIBLE, "Globally invisible" },
+  { USERLIST_NN_IS_BANNED, "Globally banned" },
+  { USERLIST_NN_IS_LOCKED, "Globally locked" },
+  { USERLIST_NN_SHOW_LOGIN, "Show login to everybody" },
+  { USERLIST_NN_SHOW_EMAIL, "Show email to everybody" },
+  { USERLIST_NN_READ_ONLY, "Globally read-only" },
+  { USERLIST_NN_NEVER_CLEAN, "Do not auto-clean" },
+  { USERLIST_NN_SIMPLE_REGISTRATION, "Simple registration" },
+  { 0, 0 },
+};
+
+static const struct user_row_info user_timestamp_rows[] =
+{
+  { USERLIST_NN_REGISTRATION_TIME, "Registration time" },
+  { USERLIST_NN_LAST_LOGIN_TIME, "Last login time" },
+  { USERLIST_NN_LAST_CHANGE_TIME, "Last change time" },
+  { USERLIST_NN_LAST_PWDCHANGE_TIME, "Last password change time" },
+  { 0, 0 },
+};
+
+static const struct user_row_info user_info_rows[] =
+{
+  { USERLIST_NC_INST, "Institution name" },
+  { USERLIST_NC_INST_EN, "Inst. name (En)" },
+  { USERLIST_NC_INSTSHORT, "Short inst. name" },
+  { USERLIST_NC_INSTSHORT_EN, "Short inst. name (En)" },
+  { USERLIST_NC_INSTNUM, "Institution number" },
+  { USERLIST_NC_FAC, "Faculty name" },
+  { USERLIST_NC_FAC_EN, "Faculty name (En)" },
+  { USERLIST_NC_FACSHORT, "Short faculty name" },
+  { USERLIST_NC_FACSHORT_EN, "Short faculty name (En)" },
+  { USERLIST_NC_HOMEPAGE, "Web home page" },
+  { USERLIST_NC_CITY, "City" },
+  { USERLIST_NC_CITY_EN, "City (En)" },
+  { USERLIST_NC_COUNTRY, "Country" },
+  { USERLIST_NC_COUNTRY_EN, "Country (En)" },
+  { USERLIST_NC_REGION, "Region" },
+  { USERLIST_NC_AREA, "Region (En)" },
+  { USERLIST_NC_ZIP, "Zip code" },
+  { USERLIST_NC_STREET, "Street address" },
+  { USERLIST_NC_LOCATION, "Computer location" },
+  { USERLIST_NC_SPELLING, "Name spelling" },
+  { USERLIST_NC_PRINTER_NAME, "Printer name" },
+  { USERLIST_NC_EXAM_ID, "Examination Id" },
+  { USERLIST_NC_EXAM_CYPHER, "Examination cypher" },
+  { USERLIST_NC_LANGUAGES, "Programming languages" },
+  { USERLIST_NC_PHONE, "Contact phone" },
+  { USERLIST_NC_FIELD0, "Additional field 0" },
+  { USERLIST_NC_FIELD1, "Additional field 1" },
+  { USERLIST_NC_FIELD2, "Additional field 2" },
+  { USERLIST_NC_FIELD3, "Additional field 3" },
+  { USERLIST_NC_FIELD4, "Additional field 4" },
+  { USERLIST_NC_FIELD5, "Additional field 5" },
+  { USERLIST_NC_FIELD6, "Additional field 6" },
+  { USERLIST_NC_FIELD7, "Additional field 7" },
+  { USERLIST_NC_FIELD8, "Additional field 8" },
+  { USERLIST_NC_FIELD9, "Additional field 9" },
+
+  { 0, 0 },
+};
+
+static const struct user_row_info user_info_stat_rows[] =
+{
+  { USERLIST_NC_CREATE_TIME, "Create time" },
+  { USERLIST_NC_LAST_LOGIN_TIME, "Last login time" },
+  { USERLIST_NC_LAST_CHANGE_TIME, "Last change time" },
+  { USERLIST_NC_LAST_PWDCHANGE_TIME, "Last password change time" },
+
+  { 0, 0 },
+};
+
+static const struct user_row_info member_rows[] =
+{
+  { USERLIST_NM_FIRSTNAME, "First name" },
+  { USERLIST_NM_FIRSTNAME_EN, "First name (En)" },
+  { USERLIST_NM_MIDDLENAME, "Middle name" },
+  { USERLIST_NM_MIDDLENAME_EN, "Middle name (En)" },
+  { USERLIST_NM_SURNAME, "Surname" },
+  { USERLIST_NM_SURNAME_EN, "Surname (En)" },
+  { USERLIST_NM_GROUP, "Academic group" },
+  { USERLIST_NM_GROUP_EN, "Academic group (En)" },
+  { USERLIST_NM_EMAIL, "Email" },
+  { USERLIST_NM_HOMEPAGE, "Web home page" },
+  { USERLIST_NM_OCCUPATION, "Occupation" },
+  { USERLIST_NM_OCCUPATION_EN, "Occupation (En)" },
+  { USERLIST_NM_DISCIPLINE, "Discipline" },
+  { USERLIST_NM_INST, "Institution name" },
+  { USERLIST_NM_INST_EN, "Institution name (En)" },
+  { USERLIST_NM_INSTSHORT, "Short inst. name" },
+  { USERLIST_NM_INSTSHORT_EN, "Short inst. name (En)" },
+  { USERLIST_NM_FAC, "Faculty name" },
+  { USERLIST_NM_FAC_EN, "Faculty name (En)" },
+  { USERLIST_NM_FACSHORT, "Short faculty name" },
+  { USERLIST_NM_FACSHORT_EN, "Short faculty name (En)" },
+  { USERLIST_NM_PHONE, "Phone" },
+
+  { 0, 0 },
+};
+
+static const struct user_row_info member_date_rows[] =
+{
+  { USERLIST_NM_BIRTH_DATE, "Date of birth" },
+  { USERLIST_NM_ENTRY_DATE, "Date of entry" },
+  { USERLIST_NM_GRADUATION_DATE, "Graduation date" },
+
+  { 0, 0 },
+};
+
+static const struct user_row_info member_time_rows[] =
+{
+  { USERLIST_NM_CREATE_TIME, "Create time" },
+  { USERLIST_NM_LAST_CHANGE_TIME, "Last change time" },
+
+  { 0, 0 },
+};
+
+int
+super_serve_op_USER_DETAIL_PAGE(
+        FILE *log_f,
+        FILE *out_f,
+        struct super_http_request_info *phr)
+{
+  int retval = 0, r, row, role, pers, reg_count, cookie_count;
+  int other_user_id = 0, contest_id = 0, group_id = 0;
+  unsigned char contest_id_str[128];
+  unsigned char group_id_str[128];
+  unsigned char buf[1024];
+  unsigned char buf2[1024];
+  unsigned char hbuf[1024];
+  unsigned char *xml_text = 0;
+  struct userlist_user *u = 0;
+  struct userlist_user_info *ui = 0;
+  const unsigned char *cl, *s, *s2;
+  struct html_armor_buffer ab = HTML_ARMOR_INITIALIZER;
+  struct userlist_member *m;
+  const struct contest_desc *cnts = 0;
+  struct userlist_contest *reg;
+  struct userlist_cookie *cookie;
+  opcap_t gcaps = 0;
+  opcap_t caps = 0;
+
+  if (ss_cgi_param_int(phr, "other_user_id", &other_user_id) < 0) {
+    FAIL(S_ERR_INV_USER_ID);
+  }
+  ss_cgi_param_int_opt(phr, "contest_id", &contest_id, 0);
+  ss_cgi_param_int_opt(phr, "group_id", &group_id, 0);
+
+  if (contest_id < 0) contest_id = 0;
+  if (contest_id > 0) {
+    if (contests_get(contest_id, &cnts) < 0 || !cnts) contest_id = 0;
+  }
+  contest_id_str[0] = 0;
+  if (contest_id > 0) {
+    snprintf(contest_id_str, sizeof(contest_id_str), "&amp;contest_id=%d", contest_id);
+  }
+  if (group_id < 0) group_id = 0;
+  group_id_str[0] = 0;
+  if (group_id > 0) {
+    snprintf(group_id_str, sizeof(group_id_str), "&amp;group_id=%d", group_id);
+  }
+
+  if (get_global_caps(phr, &gcaps) >= 0 && opcaps_check(gcaps, OPCAP_GET_USER) >= 0) {
+    // this user can view the full user list and the user list for any contest
+  } else if (!cnts) {
+    // user without global OPCAP_LIST_USERS capability cannot view the full user list
+    FAIL(-S_ERR_PERM_DENIED);
+  } else if (get_contest_caps(phr, cnts, &caps) < 0 || opcaps_check(caps, OPCAP_GET_USER) < 0) {
+    FAIL(-S_ERR_PERM_DENIED);
+  }
+
+  snprintf(buf, sizeof(buf), "serve-control: %s, viewing user %d",
+           phr->html_name, other_user_id);
+  ss_write_html_header(out_f, phr, buf, 0, NULL);
+
+  fprintf(out_f, "<script language=\"javascript\">\n");
+  fprintf(out_f,
+          "function toggleRowsVisibility(value, rows1, rows2)\n"
+          "{\n"
+          "  var vis1 = \"\";\n"
+          "  var vis2 = \"\";\n"
+          "  if (value == true) {\n"
+          "    vis1 = \"none\";\n"
+          "  } else {\n"
+          "    vis2 = \"none\";\n"
+          "  }\n"
+          "  if (rows1 != null) {\n"
+          "    for (var row in rows1) {\n"
+          "      var obj = document.getElementById(rows1[row]);\n"
+          "      if (obj != null) {\n"
+          "        obj.style.display = vis1;\n"
+          "      }\n"
+          "    }\n"
+          "  }\n"
+          "  if (rows2 != null) {\n"
+          "    for (var row in rows2) {\n"
+          "      var obj = document.getElementById(rows2[row]);\n"
+          "      if (obj != null) {\n"
+          "        obj.style.display = vis2;\n"
+          "      }\n"
+          "    }\n"
+          "  }\n"
+          "}\n"
+          "function toggleRowsVisibility2(value, tid, rowclass1, rowclass2)\n"
+          "{\n"
+          "  var vis1 = \"\";\n"
+          "  var vis2 = \"\";\n"
+          "  if (value == true) {\n"
+          "    vis1 = \"none\";\n"
+          "  } else {\n"
+          "    vis2 = \"none\";\n"
+          "  }\n"
+          "  var tobj = document.getElementById(tid);\n"
+          "  if (tobj == null) {\n"
+          "    return;\n"
+          "  }\n"
+          "  var trows = tobj.rows;\n"
+          "  if (trows != null) {\n"
+          "    for (var row in trows) {\n"
+          "      if (trows[row].className == rowclass1) {\n"
+          "        trows[row].style.display = vis1;\n"
+          "      } else if (trows[row].className == rowclass2) {\n"
+          "        trows[row].style.display = vis2;\n"
+          "      }\n"
+          "    }\n"
+          "  }\n"
+          "}\n"
+          "function toggleStatVisibility(value)\n"
+          "{\n"
+          "  toggleRowsVisibility2(value, \"UserData\", \"StatRow1\", \"StatRow2\");\n"
+          "}\n"
+          "function toggleFlagVisibility(value)\n"
+          "{\n"
+          "  toggleRowsVisibility2(value, \"UserData\", \"FlagRow1\", \"FlagRow2\");\n"
+          "}\n"
+          "function toggleUserInfoVisibility(value)\n"
+          "{\n"
+          "  toggleRowsVisibility2(value, \"UserData\", \"UserInfoRow1\", \"UserInfoRow2\");\n"
+          "}\n"
+          "function toggleMemberInfoVisibility(value)\n"
+          "{\n"
+          "  toggleRowsVisibility2(value, \"UserData\", \"MemberInfoRow1\", \"MemberInfoRow2\");\n"
+          "}\n"
+          "function showContestRegs()\n"
+          "{\n"
+          "  document.getElementById(\"ContestRegsShowLink\").style.display = \"none\";\n"
+          "  document.getElementById(\"ContestRegsTable\").style.display = \"\";\n"
+          "}\n"
+          "function hideContestRegs()\n"
+          "{\n"
+          "  document.getElementById(\"ContestRegsShowLink\").style.display = \"\";\n"
+          "  document.getElementById(\"ContestRegsTable\").style.display = \"none\";\n"
+          "}\n"
+          "function showCookies()\n"
+          "{\n"
+          "  document.getElementById(\"CookiesShowLink\").style.display = \"none\";\n"
+          "  document.getElementById(\"CookiesTable\").style.display = \"\";\n"
+          "}\n"
+          "function hideCookies()\n"
+          "{\n"
+          "  document.getElementById(\"CookiesShowLink\").style.display = \"\";\n"
+          "  document.getElementById(\"CookiesTable\").style.display = \"none\";\n"
+          "}\n"
+          "function checkNull(field_id)\n"
+          "{\n"
+          "  var form_obj = document.getElementById(\"UserForm\");\n"
+          "  var checkbox_obj = form_obj[\"field_null_\" + field_id];\n"
+          "  var text_obj = form_obj[\"field_\" + field_id];\n"
+          "  if (checkbox_obj != null && checkbox_obj.checked) {\n"
+          "    if (text_obj) text_obj.value = \"\";\n"
+          "  }\n"
+          "}\n"
+          "function uncheckNull(field_id)\n"
+          "{\n"
+          "  var form_obj = document.getElementById(\"UserForm\");\n"
+          "  var checkbox_obj = form_obj[\"field_null_\" + field_id];\n"
+          "  var text_obj = form_obj[\"field_\" + field_id];\n"
+          "  if (text_obj != null && (text_obj.value != null && text_obj.value != \"\")) {\n"
+          "    if (checkbox_obj != null) checkbox_obj.checked = false;\n"
+          "  }\n"
+          "}\n"
+          "");
+  fprintf(out_f, "</script>\n");
+
+  fprintf(out_f, "<h1>%s</h1>\n<br/>\n", buf);
+
+  print_top_navigation_links(log_f, out_f, phr, contest_id, group_id, other_user_id, NULL);
+
+  if (!(u = get_user_info(phr, other_user_id, contest_id))) FAIL(S_ERR_DB_ERROR);
+  ui = u->cnts0;
+
+  html_start_form_id(out_f, 1, phr->self_url, "UserForm", "");
+  html_hidden(out_f, "SID", "%016llx", phr->session_id);
+  html_hidden(out_f, "action", "%d", SSERV_CMD_HTTP_REQUEST);
+  html_hidden(out_f, "other_user_id", "%d", other_user_id);
+  if (contest_id > 0) {
+    html_hidden(out_f, "contest_id", "%d", contest_id);
+  }
+  if (group_id > 0) {
+    html_hidden(out_f, "group_id", "%d", group_id);
+  }
+  cl = " class=\"b1\"";
+  fprintf(out_f, "<table%s id=\"UserData\">\n", cl);
+  fprintf(out_f, "<tr><td%s colspan=\"4\" align=\"center\">", cl);
+  fprintf(out_f, "%s%s</a>",
+          html_hyperref(hbuf, sizeof(hbuf), phr->session_id,
+                        phr->self_url, NULL,
+                        "action=%d&amp;op=%d&amp;other_user_id=%d%s%s",
+                        SSERV_CMD_HTTP_REQUEST, SSERV_OP_USER_CANCEL_AND_PREV_ACTION,
+                        other_user_id, contest_id_str, group_id_str),
+          "Prev user");
+  fprintf(out_f, "&nbsp;%s%s</a>",
+          html_hyperref(hbuf, sizeof(hbuf), phr->session_id,
+                        phr->self_url, NULL,
+                        "action=%d&amp;op=%d&amp;other_user_id=%d%s%s",
+                        SSERV_CMD_HTTP_REQUEST, SSERV_OP_USER_CANCEL_AND_NEXT_ACTION,
+                        other_user_id, contest_id_str, group_id_str),
+          "Next user");
+  fprintf(out_f, "</td></tr>\n");
+  fprintf(out_f, "<tr><th%s width=\"250px\">&nbsp;</th><th%s><b>NULL?</b></th><th%s>&nbsp;</th><th%s>&nbsp;</th></tr>\n", cl, cl, cl, cl);
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>&nbsp;</td><td%s>%d</td><td%s>&nbsp;</td></tr>\n",
+          cl, "User ID", cl, cl, other_user_id, cl);
+  s = u->login;
+  if (!s) s = "";
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>&nbsp;</td><td%s>%s</td><td%s>&nbsp;</td></tr>\n",
+          cl, "User login", cl, cl, 
+          html_input_text(buf, sizeof(hbuf), "other_login", 50, "%s", ARMOR(s)), cl);
+  s = u->email;
+  if (!s) s = "";
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>&nbsp;</td><td%s>%s</td><td%s>&nbsp;</td></tr>\n",
+          cl, "User e-mail", cl, cl, 
+          html_input_text(buf, sizeof(buf), "email", 50, "%s", ARMOR(s)), cl);
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>&nbsp;</td><td%s>",
+          cl, "Password", cl, cl);
+  if (!u->passwd) {
+    fprintf(out_f, "<i>NULL</i>");
+  } else if (u->passwd_method == USERLIST_PWD_PLAIN) {
+    fprintf(out_f, "<tt>%s</tt>", ARMOR(u->passwd));
+  } else if (u->passwd_method == USERLIST_PWD_SHA1) {
+    fprintf(out_f, "<i>Hashed with SHA1</i>");
+  } else {
+    fprintf(out_f, "<i>Unsupported method</i>");
+  }
+  fprintf(out_f, "</td><td%s>%s%s</a></td></tr>", cl,
+          html_hyperref(hbuf, sizeof(hbuf), phr->session_id, phr->self_url,
+                        NULL, "action=%d&amp;op=%d&amp;other_user_id=%d&amp;next_op=%d%s%s",
+                        SSERV_CMD_HTTP_REQUEST, SSERV_OP_USER_PASSWORD_PAGE,
+                        other_user_id, SSERV_OP_USER_DETAIL_PAGE, contest_id_str, group_id_str),
+          "[Change]");
+  fprintf(out_f, "<tr class=\"StatRow1\"><td colspan=\"4\"%s align=\"center\"><a onclick=\"toggleStatVisibility(true)\">[%s]</a></td></tr>\n",
+          cl, "Show user statistics");
+  fprintf(out_f, "<tr class=\"StatRow2\" style=\"display: none;\"><td colspan=\"4\"%s align=\"center\"><a onclick=\"toggleStatVisibility(false)\">[%s]</a></td></tr>\n", cl, "Hide user statistics");
+
+  for (row = 0; user_timestamp_rows[row].field_id > 0; ++row) {
+    fprintf(out_f, "<tr class=\"StatRow2\" style=\"display: none;\"><td%s><b>%s:</b></td><td%s>&nbsp;</td><td%s>",
+            cl, user_timestamp_rows[row].field_desc, cl, cl);
+    time_t *pt = (time_t*) userlist_get_user_field_ptr(u, user_timestamp_rows[row].field_id);
+    if (pt && *pt > 0) {
+      fprintf(out_f, "%s</td><td%s>%s%s</a></td></tr>\n",
+              xml_unparse_date(*pt), cl,
+              html_hyperref(hbuf, sizeof(hbuf), phr->session_id, phr->self_url,
+                            NULL, "action=%d&amp;op=%d&amp;other_user_id=%d&amp;field_id=%d%s%s",
+                            SSERV_CMD_HTTP_REQUEST, SSERV_OP_USER_CLEAR_FIELD_ACTION,
+                            other_user_id, user_timestamp_rows[row].field_id,
+                            contest_id_str, group_id_str),
+              "[Reset]");
+    } else if (pt) {
+      fprintf(out_f, "<i>Not set</i></td><td%s>&nbsp;</td></tr>\n", cl);
+    } else {
+      fprintf(out_f, "<i>Invalid field</i></td><td%s>&nbsp;</td></tr>\n", cl);
+    }
+  }
+
+  fprintf(out_f, "<tr class=\"FlagRow1\"><td colspan=\"4\"%s align=\"center\"><a onclick=\"toggleFlagVisibility(true)\">[%s]</a></td></tr>\n",
+          cl, "Show user flags");
+  fprintf(out_f, "<tr class=\"FlagRow2\" style=\"display: none;\"><td colspan=\"4\"%s align=\"center\"><a onclick=\"toggleFlagVisibility(false)\">[%s]</a></td></tr>\n", cl, "Hide user flags");
+
+  for (row = 0; user_flag_rows[row].field_id > 0; ++row) {
+    fprintf(out_f, "<tr class=\"FlagRow2\" style=\"display: none;\"><td%s><b>%s:</b></td><td%s>&nbsp;</td><td%s>",
+            cl, user_flag_rows[row].field_desc, cl, cl);
+    int *pi = (int*) userlist_get_user_field_ptr(u, user_flag_rows[row].field_id);
+    if (pi) {
+      s = "";
+      if (*pi > 0) {
+        s = " checked=\"checked\"";
+      }
+      fprintf(out_f, "<input type=\"checkbox\" name=\"field_%d\" value=\"1\"%s />",
+              user_flag_rows[row].field_id, s);
+    } else {
+      fprintf(out_f, "<i>Invalid field</i>");
+    }
+    fprintf(out_f, "</td><td%s>&nbsp;</td></tr>\n", cl);
+  }
+
+  fprintf(out_f, "<tr><td%s align=\"center\" colspan=\"4\"><b>%s</b></td></tr>\n",
+          cl, "Generic contest-specific fields");
+  s = "";
+  if (ui && ui->cnts_read_only > 0) s = " checked=\"checked\"";
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>&nbsp;</td><td%s><input type=\"checkbox\" name=\"field_%d\" value=\"1\"%s /></td><td%s>&nbsp;</td></tr>\n",
+          cl, "User data is read-only", cl, cl, USERLIST_NC_CNTS_READ_ONLY, s, cl);
+  s = "";
+  s2 = 0;
+  if (ui) s2 = ui->name;
+  if (!s2) {
+    s = " checked=\"checked\"";
+    s2 = "";
+  }
+  snprintf(hbuf, sizeof(hbuf), "field_%d", USERLIST_NC_NAME);
+  snprintf(buf2, sizeof(buf2), "uncheckNull('%d')", USERLIST_NC_NAME);
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s><input type=\"checkbox\" onchange=\"checkNull('%d')\" name=\"field_null_%d\" value=\"1\"%s /></td><td%s>%s</td><td%s>&nbsp;</td></tr>\n",
+          cl, "User name", cl, USERLIST_NC_NAME, USERLIST_NC_NAME, s, cl, 
+          html_input_text_js(buf, sizeof(buf), hbuf, 50, buf2, "%s", ARMOR(s2)), cl);
+  if (contest_id > 0 && cnts && !cnts->disable_team_password) {
+    fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>&nbsp;</td><td%s>",
+            cl, "Contest password", cl, cl);
+    if (!ui || !ui->team_passwd) {
+      fprintf(out_f, "<i>NULL</i>");
+    } else if (ui->team_passwd_method == USERLIST_PWD_PLAIN) {
+      fprintf(out_f, "<tt>%s</tt>", ARMOR(ui->team_passwd));
+    } else if (ui->team_passwd_method == USERLIST_PWD_SHA1) {
+      fprintf(out_f, "<i>Hashed with SHA1</i>");
+    } else {
+      fprintf(out_f, "<i>Unsupported method</i>");
+    }
+    fprintf(out_f, "</td><td%s>%s%s</a></td></tr>", cl,
+            html_hyperref(hbuf, sizeof(hbuf), phr->session_id, phr->self_url,
+                          NULL, "action=%d&amp;op=%d&amp;other_user_id=%d&amp;contest_id=%d%s",
+                          SSERV_CMD_HTTP_REQUEST, SSERV_OP_USER_CNTS_PASSWORD_PAGE,
+                          other_user_id, contest_id, group_id_str),
+            "[Change]");
+  }
+
+  fprintf(out_f, "<tr class=\"UserInfoRow1\"><td colspan=\"4\"%s align=\"center\"><a onclick=\"toggleUserInfoVisibility(true)\">[%s]</a></td></tr>\n",
+          cl, "Show more user info fields");
+  fprintf(out_f, "<tr class=\"UserInfoRow2\" style=\"display: none;\"><td colspan=\"4\"%s align=\"center\"><a onclick=\"toggleUserInfoVisibility(false)\">[%s]</a></td></tr>\n", cl, "Hide user info fields");
+
+  for (row = 0; user_info_rows[row].field_id > 0; ++row) {
+    s = 0;
+    if (user_info_rows[row].field_id == USERLIST_NC_INSTNUM) {
+      if (ui && ui->instnum > 0) {
+        snprintf(buf2, sizeof(buf2), "%d", ui->instnum);
+        s = buf2;
+      }
+    } else if (ui) {
+      unsigned char **ps = (unsigned char**) userlist_get_user_info_field_ptr(ui, user_info_rows[row].field_id);
+      if (!ps) continue;
+      s = *ps;
+    }
+    snprintf(hbuf, sizeof(hbuf), "%d", user_info_rows[row].field_id);
+    string_row(out_f, "UserInfoRow2", 1, "b1", user_info_rows[row].field_desc, hbuf, s);
+  }
+
+  if (ui) {
+    for (row = 0; user_info_stat_rows[row].field_id > 0; ++row) {
+      fprintf(out_f, "<tr class=\"UserInfoRow2\" style=\"display: none;\"><td%s><b>%s:</b></td><td%s>&nbsp;</td><td%s>",
+              cl, user_info_stat_rows[row].field_desc, cl, cl);
+      time_t *pt = (time_t*) userlist_get_user_info_field_ptr(ui, user_info_stat_rows[row].field_id);
+      if (pt && *pt > 0) {
+        fprintf(out_f, "%s</td><td%s>%s%s</a></td></tr>\n",
+                xml_unparse_date(*pt), cl,
+                html_hyperref(hbuf, sizeof(hbuf), phr->session_id, phr->self_url,
+                              NULL, "action=%d&amp;op=%d&amp;other_user_id=%d&amp;field_id=%d%s%s",
+                              SSERV_CMD_HTTP_REQUEST, SSERV_OP_USER_CLEAR_FIELD_ACTION,
+                              other_user_id, user_info_stat_rows[row].field_id,
+                              contest_id_str, group_id_str),
+                "[Reset]");
+      } else if (pt) {
+        fprintf(out_f, "<i>Not set</i></td><td%s>&nbsp;</td></tr>\n", cl);
+      } else {
+        fprintf(out_f, "<i>Invalid field</i></td><td%s>&nbsp;</td></tr>\n", cl);
+      }
+    }
+  }
+
+  fprintf(out_f, "<tr class=\"MemberInfoRow1\"><td colspan=\"4\"%s align=\"center\"><a onclick=\"toggleMemberInfoVisibility(true)\">[%s]</a></td></tr>\n",
+          cl, "Show members");
+  fprintf(out_f, "<tr class=\"MemberInfoRow2\" style=\"display: none;\"><td colspan=\"4\"%s align=\"center\"><a onclick=\"toggleMemberInfoVisibility(false)\">[%s]</a></td></tr>\n", cl, "Hide members");
+
+  if (ui && ui->members) {
+    for (role = 0; role < CONTEST_LAST_MEMBER; ++role) {
+      int role_cnt = userlist_members_count(ui->members, role);
+      if (role_cnt <= 0) continue;
+      fprintf(out_f, "<tr class=\"MemberInfoRow2\" style=\"display: none;\"><td colspan=\"4\"%s align=\"center\"><b>%s (%d)</b></td></tr>\n", cl, member_string_pl[role], role_cnt);
+      for (pers = 0; pers < role_cnt; ++pers) {
+        if (!(m = (struct userlist_member*) userlist_members_get_nth(ui->members, role, pers)))
+          continue;
+
+        fprintf(out_f, "<tr class=\"MemberInfoRow2\" style=\"display: none;\"><td colspan=\"3\"%s align=\"center\"><b>%s %d (%d)</b></td><td%s>%s[%s]</a></tr>\n", cl, member_string[role], pers + 1, m->serial, cl,
+                html_hyperref(hbuf, sizeof(hbuf), phr->session_id, phr->self_url,
+                              NULL, "action=%d&amp;op=%d&amp;other_user_id=%d&amp;serial=%d%s%s",
+                              SSERV_CMD_HTTP_REQUEST, SSERV_OP_USER_DELETE_MEMBER_PAGE,
+                              other_user_id, m->serial, contest_id_str, group_id_str),
+                "Delete");
+
+        fprintf(out_f, "<tr class=\"MemberInfoRow2\" style=\"display: none;\"><td%s><b>%s</b></td><td%s>&nbsp;</td><td%s>%d</td><td%s>&nbsp;</td></tr>\n",
+                cl, "Member serial Id", cl, cl, m->serial, cl);
+
+        snprintf(hbuf, sizeof(hbuf), "field_%d_%d", m->serial, USERLIST_NM_STATUS);
+        fprintf(out_f, "<tr class=\"MemberInfoRow2\" style=\"display: none;\"><td%s><b>%s</b></td><td%s>&nbsp;</td><td%s>",
+                cl, "Status", cl, cl);
+        ss_select(out_f, hbuf, (const unsigned char* []) { "Undefined", "School student", "Student", "Magistrant", "PhD student", "School teacher", "Professor", "Scientist", "Other", NULL }, m->status);
+        fprintf(out_f, "</td><td%s>&nbsp;</td></tr>\n", cl);
+        snprintf(hbuf, sizeof(hbuf), "field_%d_%d", USERLIST_NM_GENDER, m->serial);
+        fprintf(out_f, "<tr class=\"MemberInfoRow2\" style=\"display: none;\"><td%s><b>%s</b></td><td%s>&nbsp;</td><td%s>",
+                cl, "Status", cl, cl);
+        ss_select(out_f, hbuf, (const unsigned char* []) { "Undefined", "Male", "Female", NULL }, m->gender);
+        fprintf(out_f, "</td><td%s>&nbsp;</td></tr>\n", cl);
+
+        s = 0;
+        if (m->grade > 0) {
+          snprintf(buf2, sizeof(buf2), "%d", m->grade);
+          s = buf2;
+        }
+        snprintf(hbuf, sizeof(hbuf), "%d_%d", USERLIST_NM_GRADE, m->serial);
+        string_row(out_f, "MemberInfoRow2", 1, "b1", "Grade", hbuf, s);
+
+        for (row = 0; member_rows[row].field_id > 0; ++row) {
+          unsigned char **ps = (unsigned char**) userlist_get_member_field_ptr(m, member_rows[row].field_id);
+          if (!ps) continue;
+          s = *ps;
+          snprintf(hbuf, sizeof(hbuf), "%d_%d", member_rows[row].field_id, m->serial);
+          string_row(out_f, "MemberInfoRow2", 1, "b1", member_rows[row].field_desc, hbuf, s);
+        }
+
+        for (row = 0; member_date_rows[row].field_id > 0; ++row) {
+          time_t *pt = (time_t*) userlist_get_member_field_ptr(m, member_date_rows[row].field_id);
+          if (!pt) continue;
+          s = 0;
+          if (*pt > 0) {
+            userlist_get_member_field_str(buf2, sizeof(buf2), m, member_date_rows[row].field_id, 0, 0);
+            s = buf2;
+          }
+          snprintf(hbuf, sizeof(hbuf), "%d_%d", member_date_rows[row].field_id, m->serial);
+          string_row(out_f, "MemberInfoRow2", 1, "b1", member_date_rows[row].field_desc, hbuf, s);
+        }
+
+        for (row = 0; member_time_rows[row].field_id > 0; ++row) {
+          fprintf(out_f, "<tr class=\"MemberInfoRow2\" style=\"display: none;\"><td%s><b>%s:</b></td><td%s>&nbsp;</td><td%s>",
+                  cl, member_time_rows[row].field_desc, cl, cl);
+          time_t *pt = (time_t*) userlist_get_member_field_ptr(m, member_time_rows[row].field_id);
+          if (pt && *pt > 0) {
+            fprintf(out_f, "%s</td><td%s>%s%s</a></td></tr>\n",
+                    xml_unparse_date(*pt), cl,
+                    html_hyperref(hbuf, sizeof(hbuf), phr->session_id, phr->self_url,
+                                  NULL, "action=%d&amp;op=%d&amp;other_user_id=%d&amp;member_id=%d&amp;field_id=%d%s%s",
+                                  SSERV_CMD_HTTP_REQUEST, SSERV_OP_USER_CLEAR_FIELD_ACTION,
+                                  other_user_id, m->serial, member_time_rows[row].field_id,
+                                  contest_id_str, group_id_str),
+                    "[Reset]");
+          } else if (pt) {
+            fprintf(out_f, "<i>Not set</i></td><td%s>&nbsp;</td></tr>\n", cl);
+          } else {
+            fprintf(out_f, "<i>Invalid field</i></td><td%s>&nbsp;</td></tr>\n", cl);
+          }
+        }
+      }
+    }
+  }
+
+  fprintf(out_f, "<tr><td%s colspan=\"4\" align=\"center\">", cl);
+  fprintf(out_f, "<input type=\"submit\" name=\"op_%d\" value=\"%s\" />",
+          SSERV_OP_USER_SAVE_AND_PREV_ACTION, "Save and goto PREV user");
+  fprintf(out_f, "&nbsp;<input type=\"submit\" name=\"op_%d\" value=\"%s\" />",
+          SSERV_OP_USER_SAVE_ACTION, "Save and goto user list");
+  fprintf(out_f, "&nbsp;<input type=\"submit\" name=\"op_%d\" value=\"%s\" />",
+          SSERV_OP_USER_SAVE_AND_NEXT_ACTION, "Save and goto NEXT user");
+  fprintf(out_f, "</td></tr>\n");
+  fprintf(out_f, "<tr><td%s colspan=\"4\" align=\"center\">", cl);
+  fprintf(out_f, "<input type=\"submit\" name=\"op_%d\" value=\"%s\" />",
+          SSERV_OP_USER_CANCEL_AND_PREV_ACTION, "Cancel and goto PREV user");
+  fprintf(out_f, "&nbsp;<input type=\"submit\" name=\"op_%d\" value=\"%s\" />",
+          SSERV_OP_USER_CANCEL_ACTION, "Cancel and goto user list");
+  fprintf(out_f, "&nbsp;<input type=\"submit\" name=\"op_%d\" value=\"%s\" />",
+          SSERV_OP_USER_CANCEL_AND_NEXT_ACTION, "Cancel and goto NEXT user");
+  fprintf(out_f, "</td></tr>\n");
+
+  fprintf(out_f, "</table>\n");
+  fprintf(out_f, "</form>\n");
+
+  html_start_form(out_f, 1, phr->self_url, "");
+  html_hidden(out_f, "SID", "%016llx", phr->session_id);
+  html_hidden(out_f, "action", "%d", SSERV_CMD_HTTP_REQUEST);
+  html_hidden(out_f, "other_user_id", "%d", other_user_id);
+  if (contest_id > 0) {
+    html_hidden(out_f, "contest_id", "%d", contest_id);
+  }
+  if (group_id > 0) {
+    html_hidden(out_f, "group_id", "%d", group_id);
+  }
+  fprintf(out_f, "Create new member: ");
+  ss_select(out_f, "role", (const unsigned char* []) { "", "Contestant", "Reserve", "Coach", "Advisor", "Guest", NULL }, 0);
+  fprintf(out_f, "<input type=\"submit\" name=\"op_%d\" value=\"%s\" />",
+          SSERV_OP_USER_CREATE_MEMBER_ACTION, "Create member");
+  fprintf(out_f, "</form>\n");
+
+  reg_count = userlist_user_count_contests(u);
+  if (reg_count > 0) {
+    fprintf(out_f, "<h2>%s</h2>\n", "Contest registrations");
+
+    fprintf(out_f, "<div id=\"ContestRegsShowLink\"><p><a onclick=\"showContestRegs()\">%s</a></p></div>\n",
+            "Show Contest Registrations");
+    fprintf(out_f, "<div id=\"ContestRegsTable\" style=\"display: none;\"><p><a onclick=\"hideContestRegs()\">%s</a></p>\n",
+            "Hide Contest Registrations");
+    fprintf(out_f, "<table%s>\n", cl);
+    fprintf(out_f, "<tr><th%s align=\"center\"><b>Contest Id</b></th><th%s align=\"center\"><b>Contest name</b></th>"
+            "<th%s align=\"center\"><b>Status</b></th><th%s align=\"center\"><b>Flags</b></th>"
+            "<th%s align=\"center\"><b>Create date</b></th><th%s align=\"center\"><b>Last change date</b></th>"
+            "<th%s align=\"center\"><b>Actions</b></th></tr>\n",
+            cl, cl, cl, cl, cl, cl, cl);
+    for (reg = FIRST_CONTEST(u); reg; reg = NEXT_CONTEST(reg)) {
+      if (contests_get(reg->id, &cnts) < 0 || !cnts) continue;
+      fprintf(out_f, "<tr>");
+      fprintf(out_f, "<td%s>%d</td>", cl, reg->id);
+      fprintf(out_f, "<td%s>%s</td>", cl, ARMOR(cnts->name));
+      r = reg->status;
+      if (r < 0 || r >= USERLIST_REG_LAST) r = USERLIST_REG_LAST;
+      fprintf(out_f, "<td%s>%s</td>", cl, reg_status_strs[r]);
+      fprintf(out_f, "<td%s>", cl);
+      r = 0;
+      if ((reg->flags & USERLIST_UC_INVISIBLE)) {
+        if (r++) fprintf(out_f, ", ");
+        fprintf(out_f, "invisible");
+      }
+      if ((reg->flags & USERLIST_UC_BANNED)) {
+        if (r++) fprintf(out_f, ", ");
+        fprintf(out_f, "banned");
+      }
+      if ((reg->flags & USERLIST_UC_LOCKED)) {
+        if (r++) fprintf(out_f, ", ");
+        fprintf(out_f, "locked");
+      }
+      if ((reg->flags & USERLIST_UC_INCOMPLETE)) {
+        if (r++) fprintf(out_f, ", ");
+        fprintf(out_f, "incomplete");
+      }
+      if ((reg->flags & USERLIST_UC_DISQUALIFIED)) {
+        if (r++) fprintf(out_f, ", ");
+        fprintf(out_f, "disqualified");
+      }
+      fprintf(out_f, "</td>");
+      if (reg->create_time > 0) {
+        fprintf(out_f, "<td%s>%s</td>", cl, xml_unparse_date(reg->create_time));
+      } else {
+        fprintf(out_f, "<td%s><i>Not set</i></td>", cl);
+      }
+      if (reg->last_change_time > 0) {
+        fprintf(out_f, "<td%s>%s</td>", cl, xml_unparse_date(reg->last_change_time));
+      } else {
+        fprintf(out_f, "<td%s><i>Not set</i></td>", cl);
+      }
+      fprintf(out_f, "<td%s>", cl);
+      fprintf(out_f, "%s[%s]</a>",
+              html_hyperref(hbuf, sizeof(hbuf), phr->session_id, phr->self_url,
+                            NULL, "action=%d&amp;op=%d&amp;other_user_id=%d&amp;contest_id=%d",
+                                  SSERV_CMD_HTTP_REQUEST, SSERV_OP_USER_DETAIL_PAGE,
+                                  other_user_id, reg->id),
+              "User details");
+      fprintf(out_f, "&nbsp;%s[%s]</a>",
+              html_hyperref(hbuf, sizeof(hbuf), phr->session_id, phr->self_url,
+                            NULL, "action=%d&amp;op=%d&amp;next_op=%d&amp;other_user_id=%d&amp;other_contest_id=%d%s%s",
+                            SSERV_CMD_HTTP_REQUEST, SSERV_OP_USER_EDIT_REG_PAGE,
+                            SSERV_OP_USER_DETAIL_PAGE,
+                            other_user_id, reg->id, contest_id_str, group_id_str),
+              "Change");
+      fprintf(out_f, "&nbsp;%s[%s]</a>",
+              html_hyperref(hbuf, sizeof(hbuf), phr->session_id, phr->self_url,
+                            NULL, "action=%d&amp;op=%d&amp;next_op=%d&amp;other_user_id=%d&amp;other_contest_id=%d%s%s",
+                            SSERV_CMD_HTTP_REQUEST, SSERV_OP_USER_DELETE_REG_PAGE,
+                            SSERV_OP_USER_DETAIL_PAGE,
+                            other_user_id, reg->id, contest_id_str, group_id_str),
+              "Delete");
+
+      fprintf(out_f, "</td>");
+      fprintf(out_f, "</tr>\n");
+    }
+    fprintf(out_f, "</table>\n");
+    fprintf(out_f, "<p>%s[%s]</a></p>",
+            html_hyperref(hbuf, sizeof(hbuf), phr->session_id, phr->self_url,
+                          NULL, "action=%d&amp;op=%d&amp;other_user_id=%d",
+                          SSERV_CMD_HTTP_REQUEST, SSERV_OP_USER_CREATE_REG_PAGE,
+                          other_user_id),
+            "Create a registration");
+    fprintf(out_f, "</div>\n");
+  }
+
+  cookie_count = userlist_user_count_cookies(u);
+  if (cookie_count > 0) {
+    fprintf(out_f, "<h2>%s</h2>\n", "Sessions");
+
+    fprintf(out_f, "<div id=\"CookiesShowLink\"><p><a onclick=\"showCookies()\">%s</a></p></div>\n",
+            "Show Cookies");
+    fprintf(out_f, "<div id=\"CookiesTable\" style=\"display: none;\"><p><a onclick=\"hideCookies()\">%s</a></p>\n",
+            "Hide Cookies");
+    fprintf(out_f, "<table%s>\n", cl);
+
+    fprintf(out_f, "<tr>");
+    fprintf(out_f, "<td%s align=\"center\"><b>%s</b></td>", cl, "IP address");
+    fprintf(out_f, "<td%s align=\"center\"><b>%s</b></td>", cl, "SSL?");
+    fprintf(out_f, "<td%s align=\"center\"><b>%s</b></td>", cl, "Session ID");
+    fprintf(out_f, "<td%s align=\"center\"><b>%s</b></td>", cl, "Expiry time");
+    fprintf(out_f, "<td%s align=\"center\"><b>%s</b></td>", cl, "Contest ID");
+    fprintf(out_f, "<td%s align=\"center\"><b>%s</b></td>", cl, "Locale ID");
+    fprintf(out_f, "<td%s align=\"center\"><b>%s</b></td>", cl, "Privilege Level");
+    fprintf(out_f, "<td%s align=\"center\"><b>%s</b></td>", cl, "Role");
+    fprintf(out_f, "<td%s align=\"center\"><b>%s</b></td>", cl, "Recovery?");
+    fprintf(out_f, "<td%s align=\"center\"><b>%s</b></td>", cl, "Team?");
+    fprintf(out_f, "<td%s align=\"center\"><b>%s</b></td>", cl, "Actions");
+    fprintf(out_f, "</tr>\n");
+
+    for (cookie=FIRST_COOKIE(u);cookie;cookie=NEXT_COOKIE(cookie)) {
+      fprintf(out_f, "<tr>");
+      fprintf(out_f, "<td%s>%s</td>", cl, xml_unparse_ip(cookie->ip));
+      fprintf(out_f, "<td%s>%d</td>", cl, cookie->ssl);
+      fprintf(out_f, "<td%s>%016llx</td>", cl, cookie->cookie);
+      fprintf(out_f, "<td%s>%s</td>", cl, xml_unparse_date(cookie->expire));
+      fprintf(out_f, "<td%s>%d</td>", cl, cookie->contest_id);
+      fprintf(out_f, "<td%s>%d</td>", cl, cookie->locale_id);
+      fprintf(out_f, "<td%s>%d</td>", cl, cookie->priv_level);
+      fprintf(out_f, "<td%s>%d</td>", cl, cookie->role);
+      fprintf(out_f, "<td%s>%d</td>", cl, cookie->recovery);
+      fprintf(out_f, "<td%s>%d</td>", cl, cookie->team_login);
+      fprintf(out_f, "<td%s>%s[%s]</a></td>", cl,
+              html_hyperref(hbuf, sizeof(hbuf), phr->session_id, phr->self_url,
+                            NULL, "action=%d&amp;op=%d&amp;other_user_id=%d&amp;other_SID=%016llx%s%s",
+                            SSERV_CMD_HTTP_REQUEST, SSERV_OP_USER_DELETE_SESSION_ACTION,
+                            other_user_id, cookie->cookie, contest_id_str, group_id_str),
+              "Delete");
+      fprintf(out_f, "</tr>");
+    }
+
+    fprintf(out_f, "</table>\n");
+    fprintf(out_f, "<p>%s[%s]</a></p>",
+            html_hyperref(hbuf, sizeof(hbuf), phr->session_id, phr->self_url,
+                          NULL, "action=%d&amp;op=%d&amp;other_user_id=%d%s%s",
+                          SSERV_CMD_HTTP_REQUEST, SSERV_OP_USER_DELETE_ALL_SESSIONS_ACTION,
+                          other_user_id, contest_id_str, group_id_str),
+            "Delete all sessions");
+    fprintf(out_f, "</div>\n");
+  }
+
+  ss_write_html_footer(out_f);
+
+cleanup:
+  userlist_free(&u->b); u = 0;
+  xfree(xml_text); xml_text = 0;
+  html_armor_free(&ab);
+  return retval;
+}
+
+static void
+print_user_info(
+        FILE *log_f,
+        FILE *out_f,
+        struct super_http_request_info *phr,
+        const struct contest_desc *cnts,
+        const struct userlist_user *u,
+        const struct userlist_user_info *ui,
+        const struct userlist_member *m,
+        int role,
+        int num)
+{
+  unsigned char *cl = " class=\"b1\"";
+  struct html_armor_buffer ab = HTML_ARMOR_INITIALIZER;
+  const unsigned char *s;
+  int row = 0;
+  unsigned char buf[1024];
+
+  fprintf(out_f, "<table%s>\n", cl);
+  fprintf(out_f, "<tr><td%s width=\"250px\" colspan=\"2\" align=\"center\"><b>%s %d</b></td></tr>\n", cl,
+          "User information for user", u->id);
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>%d</td></tr>\n",
+          cl, "User ID", cl, u->id);
+  s = u->login;
+  if (!s) s = "";
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>%s</td></tr>\n",
+          cl, "User login", cl, ARMOR(s));
+  s = u->email;
+  if (!s) s = "";
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>%s</td></tr>\n",
+          cl, "User e-mail", cl, ARMOR(s));
+
+  /*
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>&nbsp;</td><td%s>",
+          cl, "Password", cl, cl);
+  if (!u->passwd) {
+    fprintf(out_f, "<i>NULL</i>");
+  } else if (u->passwd_method == USERLIST_PWD_PLAIN) {
+    fprintf(out_f, "<tt>%s</tt>", ARMOR(u->passwd));
+  } else if (u->passwd_method == USERLIST_PWD_SHA1) {
+    fprintf(out_f, "<i>Hashed with SHA1</i>");
+  } else {
+    fprintf(out_f, "<i>Unsupported method</i>");
+  }
+  fprintf(out_f, "</td><td%s>%s%s</a></td></tr>", cl,
+          html_hyperref(hbuf, sizeof(hbuf), phr->session_id, phr->self_url,
+                        NULL, "action=%d&amp;op=%d&amp;other_user_id=%d&amp;next_op=%d%s%s",
+                        SSERV_CMD_HTTP_REQUEST, SSERV_OP_USER_PASSWORD_PAGE,
+                        other_user_id, SSERV_OP_USER_DETAIL_PAGE, contest_id_str, group_id_str),
+          "[Change]");
+  */
+
+  fprintf(out_f, "<tr><td colspan=\"2\"%s align=\"center\"><b>%s</b></td></tr>\n",
+          cl, "User statistics");
+
+  for (row = 0; user_timestamp_rows[row].field_id > 0; ++row) {
+    fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>",
+            cl, user_timestamp_rows[row].field_desc, cl);
+    time_t *pt = (time_t*) userlist_get_user_field_ptr(u, user_timestamp_rows[row].field_id);
+    if (pt && *pt > 0) {
+      fprintf(out_f, "%s</td></tr>\n", xml_unparse_date(*pt));
+    } else if (pt) {
+      fprintf(out_f, "<i>Not set</i></td></tr>\n");
+    }
+  }
+
+  for (row = 0; user_flag_rows[row].field_id > 0; ++row) {
+    fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>",
+            cl, user_flag_rows[row].field_desc, cl);
+    int *pi = (int*) userlist_get_user_field_ptr(u, user_flag_rows[row].field_id);
+    if (pi) {
+      fprintf(out_f, "%s", (*pi)?"YES":"NO");
+    } else {
+      fprintf(out_f, "<i>Invalid field</i>");
+    }
+    fprintf(out_f, "</td></tr>\n");
+  }
+
+  if (!ui || !cnts) goto cleanup;
+
+  fprintf(out_f, "<tr><td%s align=\"center\" colspan=\"2\"><b>%s %d</b></td></tr>\n",
+          cl, "Contest-specific fields for contest", cnts->id);
+
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>%s</td></tr>\n",
+          cl, "User data is read-only", cl, ui->cnts_read_only?"YES":"NO");
+  s = 0;
+  if (ui) s = ui->name;
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>%s</td></tr>\n",
+          cl, "User name", cl, ARMOR(s));
+
+  /*
+  if (contest_id > 0 && cnts && !cnts->disable_team_password) {
+    fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>&nbsp;</td><td%s>",
+            cl, "Contest password", cl, cl);
+    if (!ui || !ui->team_passwd) {
+      fprintf(out_f, "<i>NULL</i>");
+    } else if (ui->team_passwd_method == USERLIST_PWD_PLAIN) {
+      fprintf(out_f, "<tt>%s</tt>", ARMOR(ui->team_passwd));
+    } else if (ui->team_passwd_method == USERLIST_PWD_SHA1) {
+      fprintf(out_f, "<i>Hashed with SHA1</i>");
+    } else {
+      fprintf(out_f, "<i>Unsupported method</i>");
+    }
+    fprintf(out_f, "</td><td%s>%s%s</a></td></tr>", cl,
+            html_hyperref(hbuf, sizeof(hbuf), phr->session_id, phr->self_url,
+                          NULL, "action=%d&amp;op=%d&amp;other_user_id=%d&amp;contest_id=%d%s",
+                          SSERV_CMD_HTTP_REQUEST, SSERV_OP_USER_CNTS_PASSWORD_PAGE,
+                          other_user_id, contest_id, group_id_str),
+            "[Change]");
+  }
+  */
+
+  for (row = 0; user_info_rows[row].field_id > 0; ++row) {
+    userlist_get_user_info_field_str(buf, sizeof(buf), ui, user_info_rows[row].field_id, 1);
+    fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>%s</td></tr>\n",
+            cl, user_info_rows[row].field_desc, cl, ARMOR(buf));
+  }
+
+  for (row = 0; user_info_stat_rows[row].field_id > 0; ++row) {
+    userlist_get_user_info_field_str(buf, sizeof(buf), ui, user_info_stat_rows[row].field_id, 1);
+    fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>%s</td></tr>\n",
+            cl, user_info_stat_rows[row].field_desc, cl, ARMOR(buf));
+  }
+  
+  if (!m) goto cleanup;
+
+  fprintf(out_f, "<tr><td%s align=\"center\" colspan=\"2\"><b>%s %s::%d (%d)</b></td></tr>\n",
+          cl, "Member", member_string[role], num + 1, m->serial);
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>%d</td></tr>\n",
+          cl, "Member serial Id", cl, m->serial);
+  userlist_get_member_field_str(buf, sizeof(buf), m, USERLIST_NM_STATUS, 1, 0);
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>%s</td></tr>\n",
+          cl, "Status", cl, ARMOR(buf));
+  userlist_get_member_field_str(buf, sizeof(buf), m, USERLIST_NM_GENDER, 1, 0);
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>%s</td></tr>\n",
+          cl, "Gender", cl, ARMOR(buf));
+  userlist_get_member_field_str(buf, sizeof(buf), m, USERLIST_NM_GRADE, 1, 0);
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>%s</td></tr>\n",
+          cl, "Grade", cl, ARMOR(buf));
+
+  for (row = 0; member_rows[row].field_id > 0; ++row) {
+    userlist_get_member_field_str(buf, sizeof(buf), m, member_rows[row].field_id, 1, 0);
+    fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>%s</td></tr>\n",
+            cl, member_rows[row].field_desc, cl, ARMOR(buf));
+  }
+
+  for (row = 0; member_date_rows[row].field_id > 0; ++row) {
+    userlist_get_member_field_str(buf, sizeof(buf), m, member_date_rows[row].field_id, 1, 0);
+    fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>%s</td></tr>\n",
+            cl, member_date_rows[row].field_desc, cl, ARMOR(buf));
+  }
+
+  for (row = 0; member_time_rows[row].field_id > 0; ++row) {
+    userlist_get_member_field_str(buf, sizeof(buf), m, member_time_rows[row].field_id, 1, 0);
+    fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>%s</td></tr>\n",
+            cl, member_time_rows[row].field_desc, cl, ARMOR(buf));
+  }
+
+  fprintf(out_f, "</table>\n");
+
+cleanup:
+  html_armor_free(&ab);
+}
+
+int
+super_serve_op_USER_PASSWORD_PAGE(
+        FILE *log_f,
+        FILE *out_f,
+        struct super_http_request_info *phr)
+{
+  int retval = 0;
+  unsigned char buf[1024];
+  int other_user_id = -1, contest_id = -1, group_id = -1, next_op = -1;
+  const struct contest_desc *cnts = 0;
+  unsigned char *xml_text = 0;
+  struct userlist_user *u = 0;
+  const unsigned char *cl = 0;
+  const unsigned char *s = 0;
+  struct html_armor_buffer ab = HTML_ARMOR_INITIALIZER;
+
+  if (ss_cgi_param_int(phr, "other_user_id", &other_user_id) < 0) {
+    FAIL(S_ERR_INV_USER_ID);
+  }
+  ss_cgi_param_int_opt(phr, "contest_id", &contest_id, 0);
+  ss_cgi_param_int_opt(phr, "group_id", &group_id, 0);
+  ss_cgi_param_int_opt(phr, "next_op", &next_op, 0);
+
+  if (contest_id < 0) contest_id = 0;
+  if (contest_id > 0) {
+    if (contests_get(contest_id, &cnts) < 0 || !cnts) contest_id = 0;
+  }
+  if (group_id < 0) group_id = 0;
+
+  if (!(u = get_user_info(phr, other_user_id, 0))) FAIL(S_ERR_DB_ERROR);
+
+  opcap_t caps = 0;
+  if (get_global_caps(phr, &caps) < 0) FAIL(S_ERR_PERM_DENIED);
+  int cap = OPCAP_EDIT_PASSWD;
+  if (is_globally_privileged(phr, u) || is_contest_privileged(cnts, u))
+    cap = OPCAP_PRIV_EDIT_PASSWD;
+  if (opcaps_check(caps, cap) < 0) FAIL(S_ERR_PERM_DENIED);
+
+  snprintf(buf, sizeof(buf), "serve-control: %s, change registration password for user %d",
+           phr->html_name, other_user_id);
+  ss_write_html_header(out_f, phr, buf, 0, NULL);
+
+  fprintf(out_f, "<script language=\"javascript\">\n");
+  fprintf(out_f,
+          "function randomChar()\n"
+          "{\n"
+          "  var str = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\";\n"
+          "  var ind = Math.floor(Math.random() * str.length);\n"
+          "  if (ind < 0 || ind >= str.length) ind = 0;\n"
+          "  return str.charAt(ind);\n"
+          "}\n"
+          "function randomString(length)\n"
+          "{\n"
+          "  var res = \"\";\n"
+          "  for (var i = 0; i < length; ++i) {\n"
+          "    res += randomChar();\n"
+          "  }\n"
+          "  return res;\n"
+          "}\n"
+          "function generateRandomRegPassword()\n"
+          "{\n"
+          "  form_obj = document.getElementById(\"PasswordForm\");\n"
+          "  form_obj.reg_random.value = randomString(16);\n"
+          "}\n"
+          "function copyRandomRegPassword()\n"
+          "{\n"
+          "  form_obj = document.getElementById(\"PasswordForm\");\n"
+          "  form_obj.reg_password1.value = form_obj.reg_random.value;\n"
+          "  form_obj.reg_password2.value = form_obj.reg_random.value;\n"
+          "}\n"
+          "");
+  fprintf(out_f, "</script>\n");
+
+  fprintf(out_f, "<h1>%s</h1>\n<br/>\n", buf);
+
+  print_top_navigation_links(log_f, out_f, phr, contest_id, group_id, other_user_id, NULL);
+
+  if (!phr->userlist_clnt) {
+    fprintf(out_f, "<hr/><h2>Error</h2>\n");
+    fprintf(out_f, "<pre>No connection to the server!</pre>\n");
+    goto do_footer;
+  }
+
+  s = 0;
+  if (u && u->cnts0) s = u->cnts0->name;
+  if (!s) s = "";
+
+  html_start_form_id(out_f, 1, phr->self_url, "PasswordForm", "");
+  html_hidden(out_f, "SID", "%016llx", phr->session_id);
+  html_hidden(out_f, "action", "%d", SSERV_CMD_HTTP_REQUEST);
+  html_hidden(out_f, "other_user_id", "%d", other_user_id);
+  if (contest_id > 0) {
+    html_hidden(out_f, "contest_id", "%d", contest_id);
+  }
+  if (group_id > 0) {
+    html_hidden(out_f, "group_id", "%d", group_id);
+  }
+  if (next_op > 0) {
+    html_hidden(out_f, "next_op", "%d", next_op);
+  }
+  html_hidden(out_f, "op", "%d", SSERV_OP_USER_CHANGE_PASSWORD_ACTION);
+  cl = " class=\"b0\"";
+  fprintf(out_f, "<table%s>\n", cl);
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>%d</td><td%s>&nbsp;</td></tr>\n",
+          cl, "User ID", cl, other_user_id, cl);
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>%s</td><td%s>&nbsp;</td></tr>\n",
+          cl, "User login", cl, ARMOR(u->login), cl);
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>%s</td><td%s>&nbsp;</td></tr>\n",
+          cl, "User name", cl, ARMOR(s), cl);
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s colspan=\"2\">",
+          cl, "Current password", cl);
+  if (!u->passwd) {
+    fprintf(out_f, "<i>NULL</i>");
+  } else if (u->passwd_method == USERLIST_PWD_PLAIN) {
+    fprintf(out_f, "<tt>%s</tt>", ARMOR(u->passwd));
+  } else if (u->passwd_method == USERLIST_PWD_SHA1) {
+    fprintf(out_f, "Sha1 hash: <i>%s</i>", ARMOR(u->passwd));
+  }
+  fprintf(out_f, "</td></tr>\n");
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s><input type=\"password\" name=\"reg_password1\" size=\"20\" /></td><td%s>&nbsp;</td></tr>\n",
+          cl, "New password", cl, cl);
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s><input type=\"password\" name=\"reg_password2\" size=\"20\" /></td><td%s>&nbsp;</td></tr>\n",
+          cl, "Confirm new password", cl, cl);
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s><input type=\"text\" name=\"reg_random\" size=\"40\" /></td><td%s><a onclick=\"generateRandomRegPassword()\">[%s]</a>&nbsp;<a onclick=\"copyRandomRegPassword()\">[%s]</a></td></tr>\n",
+          cl, "Random password", cl, cl, "Generate", "Copy");
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s><input type=\"checkbox\" name=\"usesha1\" value=\"1\" /></td><td%s>&nbsp;</td></tr>\n",
+          cl, "Use SHA1", cl, cl);
+  fprintf(out_f, "<tr><td%s>&nbsp;</td><td%s><input type=\"submit\" name=\"submit\" value=\"%s\" /></td><td%s>&nbsp;</td></tr>\n",
+          cl, cl, "Change password", cl);
+  fprintf(out_f, "</table>\n");
+  fprintf(out_f, "</form>\n");
+
+do_footer:
+  ss_write_html_footer(out_f);
+
+cleanup:
+  userlist_free(&u->b); u = 0;
+  xfree(xml_text); xml_text = 0;
+  html_armor_free(&ab);
+  return retval;
+}
+
+int
+super_serve_op_USER_CNTS_PASSWORD_PAGE(
+        FILE *log_f,
+        FILE *out_f,
+        struct super_http_request_info *phr)
+{
+  int retval = 0;
+  unsigned char buf[1024];
+  int other_user_id = -1, contest_id = -1, group_id = -1;
+  const struct contest_desc *cnts = 0;
+  unsigned char *xml_text = 0;
+  struct userlist_user *u = 0;
+  const unsigned char *cl = 0;
+  const unsigned char *s = 0;
+  struct html_armor_buffer ab = HTML_ARMOR_INITIALIZER;
+
+  if (ss_cgi_param_int(phr, "other_user_id", &other_user_id) < 0) {
+    FAIL(S_ERR_INV_USER_ID);
+  }
+  if (ss_cgi_param_int(phr, "contest_id", &contest_id) < 0) {
+    FAIL(S_ERR_INV_CONTEST);
+  }
+  ss_cgi_param_int_opt(phr, "group_id", &group_id, 0);
+
+  if (contest_id != 0) {
+    if (contests_get(contest_id, &cnts) < 0 || !cnts) {
+      FAIL(S_ERR_INV_CONTEST);
+    }
+  }
+  if (group_id < 0) group_id = 0;
+
+  if (!(u = get_user_info(phr, other_user_id, contest_id))) FAIL(S_ERR_DB_ERROR);
+
+  if (phr->priv_level <= 0) FAIL(S_ERR_PERM_DENIED);
+  opcap_t gcaps = 0;
+  get_global_caps(phr, &gcaps);
+  opcap_t caps = 0;
+  get_contest_caps(phr, cnts, &caps);
+
+  if (is_globally_privileged(phr, u)) {
+    if (opcaps_check(gcaps, OPCAP_PRIV_EDIT_PASSWD) < 0)
+      FAIL(S_ERR_PERM_DENIED);
+  } else if (is_contest_privileged(cnts, u)) {
+    if (opcaps_check(gcaps, OPCAP_PRIV_EDIT_PASSWD) < 0 && opcaps_check(caps, OPCAP_PRIV_EDIT_PASSWD) < 0)
+      FAIL(S_ERR_PERM_DENIED);
+  } else {
+    if (opcaps_check(gcaps, OPCAP_EDIT_PASSWD) < 0 && opcaps_check(caps, OPCAP_EDIT_PASSWD) < 0)
+      FAIL(S_ERR_PERM_DENIED);
+  }
+
+  snprintf(buf, sizeof(buf), "serve-control: %s, change contest password for user %d in contest %d",
+           phr->html_name, other_user_id, contest_id);
+  ss_write_html_header(out_f, phr, buf, 0, NULL);
+
+  fprintf(out_f, "<script language=\"javascript\">\n");
+  fprintf(out_f,
+          "function randomChar()\n"
+          "{\n"
+          "  var str = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\";\n"
+          "  var ind = Math.floor(Math.random() * str.length);\n"
+          "  if (ind < 0 || ind >= str.length) ind = 0;\n"
+          "  return str.charAt(ind);\n"
+          "}\n"
+          "function randomString(length)\n"
+          "{\n"
+          "  var res = \"\";\n"
+          "  for (var i = 0; i < length; ++i) {\n"
+          "    res += randomChar();\n"
+          "  }\n"
+          "  return res;\n"
+          "}\n"
+          "function generateRandomCntsPassword()\n"
+          "{\n"
+          "  form_obj = document.getElementById(\"PasswordForm\");\n"
+          "  form_obj.cnts_random.value = randomString(16);\n"
+          "}\n"
+          "function copyRandomCntsPassword()\n"
+          "{\n"
+          "  form_obj = document.getElementById(\"PasswordForm\");\n"
+          "  form_obj.cnts_password1.value = form_obj.cnts_random.value;\n"
+          "  form_obj.cnts_password2.value = form_obj.cnts_random.value;\n"
+          "}\n"
+          "");
+  fprintf(out_f, "</script>\n");
+
+  fprintf(out_f, "<h1>%s</h1>\n<br/>\n", buf);
+
+  print_top_navigation_links(log_f, out_f, phr, contest_id, group_id, other_user_id, NULL);
+
+  if (!phr->userlist_clnt) {
+    fprintf(out_f, "<hr/><h2>Error</h2>\n");
+    fprintf(out_f, "<pre>No connection to the server!</pre>\n");
+    goto do_footer;
+  }
+
+  s = 0;
+  if (u && u->cnts0) s = u->cnts0->name;
+  if (!s) s = "";
+
+  html_start_form_id(out_f, 1, phr->self_url, "PasswordForm", "");
+  html_hidden(out_f, "SID", "%016llx", phr->session_id);
+  html_hidden(out_f, "action", "%d", SSERV_CMD_HTTP_REQUEST);
+  html_hidden(out_f, "other_user_id", "%d", other_user_id);
+  if (contest_id > 0) {
+    html_hidden(out_f, "contest_id", "%d", contest_id);
+  }
+  if (group_id > 0) {
+    html_hidden(out_f, "group_id", "%d", group_id);
+  }
+  html_hidden(out_f, "op", "%d", SSERV_OP_USER_CHANGE_CNTS_PASSWORD_ACTION);
+  cl = " class=\"b0\"";
+  fprintf(out_f, "<table%s>\n", cl);
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>%d</td><td%s>&nbsp;</td></tr>\n",
+          cl, "User ID", cl, other_user_id, cl);
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>%s</td><td%s>&nbsp;</td></tr>\n",
+          cl, "User login", cl, ARMOR(u->login), cl);
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>%s</td><td%s>&nbsp;</td></tr>\n",
+          cl, "User name", cl, ARMOR(s), cl);
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>%d</td><td%s>&nbsp;</td></tr>\n",
+          cl, "Contest ID", cl, contest_id, cl);
+  if (cnts) {
+    fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>%s</td><td%s>&nbsp;</td></tr>\n",
+            cl, "Contest name", cl, ARMOR(cnts->name), cl);
+  }
+  if (cnts && cnts->disable_team_password > 0) {
+    fprintf(out_f, "<tr><td%s colspan=\"3\" align=\"center\"><b>%s</b></td></tr>\n",
+            cl, "Contest password is disabled");
+  } else {
+    fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s colspan=\"2\">",
+            cl, "Current password", cl);
+    if (!u->passwd) {
+      fprintf(out_f, "<i>NULL</i>");
+    } else if (u->passwd_method == USERLIST_PWD_PLAIN) {
+      fprintf(out_f, "<tt>%s</tt>", ARMOR(u->passwd));
+    } else if (u->passwd_method == USERLIST_PWD_SHA1) {
+      fprintf(out_f, "Sha1 hash: <i>%s</i>", ARMOR(u->passwd));
+    }
+    fprintf(out_f, "</td></tr>\n");
+    fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s><input type=\"checkbox\" name=\"useregpasswd\" value=\"1\" /></td><td%s>&nbsp;</td></tr>\n",
+            cl, "Copy from reg. password", cl, cl);
+    fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s><input type=\"checkbox\" name=\"settonull\" value=\"1\" /></td><td%s>&nbsp;</td></tr>\n",
+            cl, "Set to NULL", cl, cl);
+    fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s><input type=\"password\" name=\"cnts_password1\" size=\"20\" /></td><td%s>&nbsp;</td></tr>\n",
+            cl, "New password", cl, cl);
+    fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s><input type=\"password\" name=\"cnts_password2\" size=\"20\" /></td><td%s>&nbsp;</td></tr>\n",
+            cl, "Confirm new password", cl, cl);
+    fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s><input type=\"text\" name=\"cnts_random\" size=\"40\" /></td><td%s><a onclick=\"generateRandomCntsPassword()\">[%s]</a>&nbsp;<a onclick=\"copyRandomCntsPassword()\">[%s]</a></td></tr>\n",
+            cl, "Random password", cl, cl, "Generate", "Copy");
+    fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s><input type=\"checkbox\" name=\"usesha1\" value=\"1\" /></td><td%s>&nbsp;</td></tr>\n",
+            cl, "Use SHA1", cl, cl);
+    fprintf(out_f, "<tr><td%s>&nbsp;</td><td%s><input type=\"submit\" name=\"submit\" value=\"%s\" /></td><td%s>&nbsp;</td></tr>\n",
+            cl, cl, "Change password", cl);
+  }
+  fprintf(out_f, "</table>\n");
+  fprintf(out_f, "</form>\n");
+
+do_footer:
+  ss_write_html_footer(out_f);
+
+cleanup:
+  userlist_free(&u->b); u = 0;
+  xfree(xml_text); xml_text = 0;
+  html_armor_free(&ab);
+  return retval;
+}
+
+int
+super_serve_op_USER_CREATE_REG_PAGE(
+        FILE *log_f,
+        FILE *out_f,
+        struct super_http_request_info *phr)
+{
+  int retval = 0;
+  int other_user_id = 0, contest_id = 0, group_id = 0;
+  const struct contest_desc *cnts = 0;
+  unsigned char buf[1024];
+  unsigned char *xml_text = 0;
+  struct userlist_user *u = 0;
+  const unsigned char *cl = 0;
+  struct html_armor_buffer ab = HTML_ARMOR_INITIALIZER;
+  const int *cnts_id_list = 0;
+  int cnts_id_count, i, other_contest_id_2;
+
+  if (ss_cgi_param_int(phr, "other_user_id", &other_user_id) < 0) {
+    FAIL(S_ERR_INV_USER_ID);
+  }
+  ss_cgi_param_int_opt(phr, "contest_id", &contest_id, 0);
+  ss_cgi_param_int_opt(phr, "group_id", &group_id, 0);
+
+  if (contest_id != 0) {
+    if (contests_get(contest_id, &cnts) < 0 || !cnts) contest_id = 0;
+  }
+  if (group_id < 0) group_id = 0;
+
+  if (phr->priv_level <= 0) FAIL(S_ERR_PERM_DENIED);
+
+  snprintf(buf, sizeof(buf), "serve-control: %s, create a contest registration for user %d",
+           phr->html_name, other_user_id);
+  ss_write_html_header(out_f, phr, buf, 0, NULL);
+
+  fprintf(out_f, "<script language=\"javascript\">\n");
+  fprintf(out_f,
+          "function updateCnts1()\n"
+          "{\n"
+          "  var obj1 = document.getElementById(\"cnts1\");\n"
+          "  var obj2 = document.getElementById(\"cnts2\");\n"
+          "  var value = obj1.value;\n"
+          "  var i;\n"
+          "  for (i = 0; i < obj2.options.length; ++i) {\n"
+          "    if (obj2.options[i].value == value) {\n"
+          "      obj2.options.selectedIndex = i;\n"
+          "      break;\n"
+          "    }\n"
+          "  }\n"
+          "}\n");
+  fprintf(out_f,
+          "function updateCnts2()\n"
+          "{\n"
+          "  var obj1 = document.getElementById(\"cnts1\");\n"
+          "  var obj2 = document.getElementById(\"cnts2\");\n"
+          "  var value = obj2.options[obj2.selectedIndex].value;\n"
+          "  obj1.value = value;\n"
+          "}\n");
+  fprintf(out_f, "</script>\n");
+
+  fprintf(out_f, "<h1>%s</h1>\n<br/>\n", buf);
+
+  print_top_navigation_links(log_f, out_f, phr, contest_id, group_id, other_user_id, NULL);
+
+  if (!(u = get_user_info(phr, other_user_id, contest_id)))
+    FAIL(S_ERR_DB_ERROR);
+
+  cnts_id_count = contests_get_list(&cnts_id_list);
+  if (cnts_id_count <= 0 || !cnts_id_list) {
+    fprintf(out_f, "<hr/><h2>Error</h2>\n");
+    fprintf(out_f, "<pre>No contests available</pre>\n");
+    goto do_footer;
+  }
+
+  html_start_form(out_f, 1, phr->self_url, "");
+  html_hidden(out_f, "SID", "%016llx", phr->session_id);
+  html_hidden(out_f, "action", "%d", SSERV_CMD_HTTP_REQUEST);
+  html_hidden(out_f, "other_user_id", "%d", other_user_id);
+  if (contest_id > 0) {
+    html_hidden(out_f, "contest_id", "%d", contest_id);
+  }
+  if (group_id > 0) {
+    html_hidden(out_f, "group_id", "%d", group_id);
+  }
+  html_hidden(out_f, "op", "%d", SSERV_OP_USER_CREATE_REG_ACTION);
+  cl = " class=\"b0\"";
+  fprintf(out_f, "<table%s>\n", cl);
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>%d</td></tr>\n",
+          cl, "User ID", cl, other_user_id);
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>%s</td></tr>\n",
+          cl, "User login", cl, ARMOR(u->login));
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s><input id=\"cnts1\" onchange=\"updateCnts1()\" type=\"text\" name=\"other_contest_id_1\" size=\"20\"/></td></tr>\n",
+          cl, "Contest ID", cl);
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>", cl, "Contest name", cl);
+  fprintf(out_f, "<select id=\"cnts2\" onchange=\"updateCnts2()\" name=\"other_contest_id_2\"><option value=\"0\"></option>");
+  for (i = 0; i < cnts_id_count; ++i) {
+    other_contest_id_2 = cnts_id_list[i];
+    if (other_contest_id_2 <= 0) continue;
+    if (contests_get(other_contest_id_2, &cnts) < 0 || !cnts) continue;
+    if (cnts->closed) continue;
+    fprintf(out_f, "<option value=\"%d\">%s</option>", other_contest_id_2, ARMOR(cnts->name));
+  }
+  fprintf(out_f, "</select>");
+  fprintf(out_f, "</td></tr>\n");
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>", cl, "Status", cl);
+  ss_select(out_f, "status", (const unsigned char* []) { "OK", "Pending", "Rejected", NULL }, 1);
+  fprintf(out_f, "</td></tr>\n");
+  fprintf(out_f, "<tr><td%s><b>%s</td></td><td%s><input type=\"checkbox\" value=\"1\" name=\"%s\" /></td></tr>\n",
+          cl, "Invisible?", cl, "is_invisible");
+  fprintf(out_f, "<tr><td%s><b>%s</td></td><td%s><input type=\"checkbox\" value=\"1\" name=\"%s\" /></td></tr>\n",
+          cl, "Banned?", cl, "is_banned");
+  fprintf(out_f, "<tr><td%s><b>%s</td></td><td%s><input type=\"checkbox\" value=\"1\" name=\"%s\" /></td></tr>\n",
+          cl, "Locked?", cl, "is_locked");
+  fprintf(out_f, "<tr><td%s><b>%s</td></td><td%s><input type=\"checkbox\" value=\"1\" name=\"%s\" /></td></tr>\n",
+          cl, "Incomplete?", cl, "is_incomplete");
+  fprintf(out_f, "<tr><td%s><b>%s</td></td><td%s><input type=\"checkbox\" value=\"1\" name=\"%s\" /></td></tr>\n",
+          cl, "Disqualified?", cl, "is_disqualified");
+  fprintf(out_f, "<tr><td%s>&nbsp;</td><td%s><input type=\"submit\" name=\"submit\" value=\"Create registration\" /></td></tr>\n", cl, cl);
+  fprintf(out_f, "</table>\n");
+  fprintf(out_f, "</form>\n");
+
+do_footer:
+  ss_write_html_footer(out_f);
+
+cleanup:
+  userlist_free(&u->b); u = 0;
+  xfree(xml_text); xml_text = 0;
+  html_armor_free(&ab);
+  return retval;
+}
+
+int
+super_serve_op_USER_EDIT_REG_PAGE(
+        FILE *log_f,
+        FILE *out_f,
+        struct super_http_request_info *phr)
+{
+  int retval = 0, r;
+  int other_user_id = 0, other_contest_id = 0, contest_id = 0, group_id = 0, next_op = 0;
+  const struct contest_desc *cnts = 0;
+  unsigned char contest_id_str[128];
+  unsigned char group_id_str[128];
+  unsigned char next_op_str[128];
+  unsigned char buf[1024];
+  unsigned char hbuf[1024];
+  unsigned char *xml_text = 0;
+  struct userlist_user *u = 0;
+  const struct userlist_contest *reg;
+  const unsigned char *cl = 0;
+  struct html_armor_buffer ab = HTML_ARMOR_INITIALIZER;
+  int reg_count = 0;
+  const unsigned char *checked = " checked=\"checked\"";
+  const unsigned char *s = 0;
+
+  if (ss_cgi_param_int(phr, "other_user_id", &other_user_id) < 0) {
+    FAIL(S_ERR_INV_USER_ID);
+  }
+  if (ss_cgi_param_int(phr, "other_contest_id", &other_contest_id) < 0) {
+    FAIL(S_ERR_INV_CONTEST);
+  }
+  ss_cgi_param_int_opt(phr, "contest_id", &contest_id, 0);
+  ss_cgi_param_int_opt(phr, "group_id", &group_id, 0);
+  ss_cgi_param_int_opt(phr, "next_op", &next_op, 0);
+
+  if (contest_id != 0) {
+    if (contests_get(contest_id, &cnts) < 0 || !cnts) contest_id = 0;
+  }
+  contest_id_str[0] = 0;
+  if (contest_id > 0) {
+    snprintf(contest_id_str, sizeof(contest_id_str), "&amp;contest_id=%d", contest_id);
+  }
+  if (group_id < 0) group_id = 0;
+  group_id_str[0] = 0;
+  if (group_id > 0) {
+    snprintf(group_id_str, sizeof(group_id_str), "&amp;group_id=%d", group_id);
+  }
+  if (other_contest_id <= 0) {
+    FAIL(S_ERR_INV_CONTEST);
+  }
+  if (contests_get(other_contest_id, &cnts) < 0 || !cnts) {
+    FAIL(S_ERR_INV_CONTEST);
+  }
+  if (next_op != SSERV_OP_USER_DETAIL_PAGE && next_op != SSERV_OP_USER_BROWSE_PAGE) next_op = 0;
+  next_op_str[0] = 0;
+  if (next_op > 0) {
+    snprintf(next_op_str, sizeof(next_op_str), "&amp;next_op=%d", next_op);
+  }
+
+  opcap_t gcaps = 0, caps = 0;
+  get_global_caps(phr, &gcaps);
+  if (opcaps_check(gcaps, OPCAP_EDIT_USER) >= 0) gcaps |= 1LL << OPCAP_EDIT_REG;
+  if (opcaps_check(gcaps, OPCAP_PRIV_EDIT_USER) >= 0) gcaps |= 1LL << OPCAP_PRIV_EDIT_REG;
+  get_contest_caps(phr, cnts, &caps);
+  caps |= gcaps;
+  if (phr->priv_level <= 0) FAIL(S_ERR_PERM_DENIED);
+  if (opcaps_check(caps, OPCAP_EDIT_REG) < 0 && opcaps_check(caps, OPCAP_PRIV_EDIT_REG) < 0)
+    FAIL(S_ERR_PERM_DENIED);
+
+  snprintf(buf, sizeof(buf), "serve-control: %s, edit the contest registration for user %d, contest %d",
+           phr->html_name, other_user_id, other_contest_id);
+  ss_write_html_header(out_f, phr, buf, 0, NULL);
+  fprintf(out_f, "<h1>%s</h1>\n<br/>\n", buf);
+
+  print_top_navigation_links(log_f, out_f, phr, contest_id, group_id, other_user_id, NULL);
+
+  if (!phr->userlist_clnt) {
+    fprintf(out_f, "<hr/><h2>Error</h2>\n");
+    fprintf(out_f, "<pre>No connection to the server!</pre>\n");
+    goto do_footer;
+  }
+
+  if (!(u = get_user_info(phr, other_user_id, 0)))
+    FAIL(S_ERR_DB_ERROR);
+
+  if (is_globally_privileged(phr, u)) {
+    if (opcaps_check(gcaps, OPCAP_PRIV_EDIT_REG) < 0) FAIL(S_ERR_PERM_DENIED);
+  } else if (is_contest_privileged(cnts, u)) {
+    if (opcaps_check(caps, OPCAP_PRIV_EDIT_REG) < 0) FAIL(S_ERR_PERM_DENIED);
+  } else {
+    if (opcaps_check(caps, OPCAP_EDIT_REG) < 0) FAIL(S_ERR_PERM_DENIED);
+  }
+
+  if ((reg_count = userlist_user_count_contests(u)) <= 0) {
+    fprintf(out_f, "<hr/><h2>Error</h2>\n");
+    fprintf(out_f, "<pre>No contest registrations</pre>\n");
+    goto do_footer;
+  }
+  for (reg = FIRST_CONTEST(u); reg; reg = NEXT_CONTEST(reg)) {
+    if (reg->id == other_contest_id) break;
+  }
+  if (!reg) {
+    fprintf(out_f, "<hr/><h2>Error</h2>\n");
+    fprintf(out_f, "<pre>User is not registered for this contest</pre>\n");
+    goto do_footer;
+  }
+
+  html_start_form(out_f, 1, phr->self_url, "");
+  html_hidden(out_f, "SID", "%016llx", phr->session_id);
+  html_hidden(out_f, "action", "%d", SSERV_CMD_HTTP_REQUEST);
+  html_hidden(out_f, "other_user_id", "%d", other_user_id);
+  html_hidden(out_f, "other_contest_id", "%d", other_contest_id);
+  if (contest_id > 0) {
+    html_hidden(out_f, "contest_id", "%d", contest_id);
+  }
+  if (group_id > 0) {
+    html_hidden(out_f, "group_id", "%d", group_id);
+  }
+  if (next_op > 0) {
+    html_hidden(out_f, "next_op", "%d", next_op);
+  }
+  html_hidden(out_f, "op", "%d", SSERV_OP_USER_EDIT_REG_ACTION);
+  cl = " class=\"b0\"";
+  fprintf(out_f, "<table%s>\n", cl);
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>%d</td></tr>\n",
+          cl, "User ID", cl, other_user_id);
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>%s</td></tr>\n",
+          cl, "User login", cl, ARMOR(u->login));
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>%d</td></tr>\n",
+          cl, "Contest ID", cl, other_contest_id);
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>%s</td></tr>\n",
+          cl, "Contest name", cl, ARMOR(cnts->name));
+  r = reg->status;
+  if (r < 0 || r >= USERLIST_REG_LAST) r = USERLIST_REG_PENDING;
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>", cl, "Status", cl);
+  ss_select(out_f, "status", (const unsigned char* []) { "OK", "Pending", "Rejected", NULL }, r);
+  fprintf(out_f, "</td></tr>\n");
+  s = "";
+  if ((reg->flags & USERLIST_UC_INVISIBLE)) s = checked;
+  fprintf(out_f, "<tr><td%s><b>%s</td></td><td%s><input type=\"checkbox\" value=\"1\" name=\"%s\"%s /></td></tr>\n",
+          cl, "Invisible?", cl, "is_invisible", s);
+  s = "";
+  if ((reg->flags & USERLIST_UC_BANNED)) s = checked;
+  fprintf(out_f, "<tr><td%s><b>%s</td></td><td%s><input type=\"checkbox\" value=\"1\" name=\"%s\"%s /></td></tr>\n",
+          cl, "Banned?", cl, "is_banned", s);
+  s = "";
+  if ((reg->flags & USERLIST_UC_LOCKED)) s = checked;
+  fprintf(out_f, "<tr><td%s><b>%s</td></td><td%s><input type=\"checkbox\" value=\"1\" name=\"%s\"%s /></td></tr>\n",
+          cl, "Locked?", cl, "is_locked", s);
+  s = "";
+  if ((reg->flags & USERLIST_UC_INCOMPLETE)) s = checked;
+  fprintf(out_f, "<tr><td%s><b>%s</td></td><td%s><input type=\"checkbox\" value=\"1\" name=\"%s\"%s /></td></tr>\n",
+          cl, "Incomplete?", cl, "is_incomplete", s);
+  s = "";
+  if ((reg->flags & USERLIST_UC_DISQUALIFIED)) s = checked;
+  fprintf(out_f, "<tr><td%s><b>%s</td></td><td%s><input type=\"checkbox\" value=\"1\" name=\"%s\"%s /></td></tr>\n",
+          cl, "Disqualified?", cl, "is_disqualified", s);
+  fprintf(out_f, "<tr><td%s>&nbsp;</td><td%s><input type=\"submit\" name=\"submit\" value=\"Save changes\" /></td></tr>\n", cl, cl);
+  fprintf(out_f, "</table>\n");
+  fprintf(out_f, "</form>\n");
+
+  fprintf(out_f, "<p>%s[%s]</a></p>",
+          html_hyperref(hbuf, sizeof(hbuf), phr->session_id, phr->self_url,
+                        NULL, "action=%d&amp;op=%d&amp;other_user_id=%d&amp;other_contest_id=%d%s%s%s",
+                        SSERV_CMD_HTTP_REQUEST, SSERV_OP_USER_DELETE_REG_PAGE,
+                        other_user_id, other_contest_id, contest_id_str, group_id_str, next_op_str),
+          "Delete");
+
+do_footer:
+  ss_write_html_footer(out_f);
+
+cleanup:
+  userlist_free(&u->b); u = 0;
+  xfree(xml_text); xml_text = 0;
+  html_armor_free(&ab);
+  return retval;
+}
+
+int
+super_serve_op_USER_DELETE_REG_PAGE(
+        FILE *log_f,
+        FILE *out_f,
+        struct super_http_request_info *phr)
+{
+  int retval = 0, r;
+  int other_user_id = 0, other_contest_id = 0, contest_id = 0, group_id = 0, next_op = 0;
+  const struct contest_desc *cnts = 0;
+  unsigned char contest_id_str[128];
+  unsigned char group_id_str[128];
+  unsigned char next_op_str[128];
+  unsigned char buf[1024];
+  unsigned char hbuf[1024];
+  unsigned char *xml_text = 0;
+  struct userlist_user *u = 0;
+  const struct userlist_contest *reg;
+  const unsigned char *cl = 0;
+  struct html_armor_buffer ab = HTML_ARMOR_INITIALIZER;
+  int reg_count = 0;
+  const unsigned char *no = " no";
+  const unsigned char *yes = " <b>YES</b>";
+  const unsigned char *s = 0;
+
+  if (ss_cgi_param_int(phr, "other_user_id", &other_user_id) < 0) {
+    FAIL(S_ERR_INV_USER_ID);
+  }
+  if (ss_cgi_param_int(phr, "other_contest_id", &other_contest_id) < 0) {
+    FAIL(S_ERR_INV_CONTEST);
+  }
+  ss_cgi_param_int_opt(phr, "contest_id", &contest_id, 0);
+  ss_cgi_param_int_opt(phr, "group_id", &group_id, 0);
+  ss_cgi_param_int_opt(phr, "next_op", &next_op, 0);
+
+  if (contest_id != 0) {
+    if (contests_get(contest_id, &cnts) < 0 || !cnts) contest_id = 0;
+  }
+  contest_id_str[0] = 0;
+  if (contest_id > 0) {
+    snprintf(contest_id_str, sizeof(contest_id_str), "&amp;contest_id=%d", contest_id);
+  }
+  if (group_id < 0) group_id = 0;
+  group_id_str[0] = 0;
+  if (group_id > 0) {
+    snprintf(group_id_str, sizeof(group_id_str), "&amp;group_id=%d", group_id);
+  }
+  if (other_contest_id <= 0) {
+    FAIL(S_ERR_INV_CONTEST);
+  }
+  if (contests_get(other_contest_id, &cnts) < 0 || !cnts) {
+    FAIL(S_ERR_INV_CONTEST);
+  }
+  if (next_op != SSERV_OP_USER_BROWSE_PAGE && next_op != SSERV_OP_USER_DETAIL_PAGE) next_op = 0;
+  next_op_str[0] = 0;
+  if (next_op > 0) {
+    snprintf(next_op_str, sizeof(next_op_str), "&amp;next_op=%d", next_op);
+  }
+
+  if (phr->priv_level <= 0) FAIL(S_ERR_PERM_DENIED);
+  opcap_t gcaps = 0, caps = 0;
+  get_global_caps(phr, &gcaps);
+  if (opcaps_check(gcaps, OPCAP_EDIT_USER) >= 0) gcaps |= 1LL << OPCAP_DELETE_REG;
+  if (opcaps_check(gcaps, OPCAP_PRIV_EDIT_USER) >= 0) gcaps |= 1LL << OPCAP_PRIV_DELETE_REG;
+  get_contest_caps(phr, cnts, &caps);
+  caps |= gcaps;
+  if (opcaps_check(caps, OPCAP_PRIV_DELETE_REG) < 0 && opcaps_check(caps, OPCAP_DELETE_REG) < 0)
+    FAIL(S_ERR_PERM_DENIED);
+
+  snprintf(buf, sizeof(buf), "serve-control: %s, delete the contest registration for user %d, contest %d",
+           phr->html_name, other_user_id, other_contest_id);
+  ss_write_html_header(out_f, phr, buf, 0, NULL);
+  fprintf(out_f, "<h1>%s</h1>\n<br/>\n", buf);
+
+  print_top_navigation_links(log_f, out_f, phr, contest_id, group_id, other_user_id, NULL);
+
+  if (!(u = get_user_info(phr, other_user_id, 0))) FAIL(S_ERR_DB_ERROR);
+
+  if (is_globally_privileged(phr, u)) {
+    if (opcaps_check(gcaps, OPCAP_PRIV_DELETE_REG) < 0) FAIL(S_ERR_PERM_DENIED);
+  } else if (is_contest_privileged(cnts, u)) {
+    if (opcaps_check(caps, OPCAP_PRIV_DELETE_REG) < 0) FAIL(S_ERR_PERM_DENIED);
+  } else {
+    if (opcaps_check(caps, OPCAP_DELETE_REG) < 0) FAIL(S_ERR_PERM_DENIED);
+  }
+
+  if ((reg_count = userlist_user_count_contests(u)) <= 0) {
+    fprintf(out_f, "<hr/><h2>Error</h2>\n");
+    fprintf(out_f, "<pre>No contest registrations</pre>\n");
+    goto do_footer;
+  }
+  for (reg = FIRST_CONTEST(u); reg; reg = NEXT_CONTEST(reg)) {
+    if (reg->id == other_contest_id) break;
+  }
+  if (!reg) {
+    fprintf(out_f, "<hr/><h2>Error</h2>\n");
+    fprintf(out_f, "<pre>User is not registered for this contest</pre>\n");
+    goto do_footer;
+  }
+
+  html_start_form(out_f, 1, phr->self_url, "");
+  html_hidden(out_f, "SID", "%016llx", phr->session_id);
+  html_hidden(out_f, "action", "%d", SSERV_CMD_HTTP_REQUEST);
+  html_hidden(out_f, "other_user_id", "%d", other_user_id);
+  html_hidden(out_f, "other_contest_id", "%d", other_contest_id);
+  if (contest_id > 0) {
+    html_hidden(out_f, "contest_id", "%d", contest_id);
+  }
+  if (group_id > 0) {
+    html_hidden(out_f, "group_id", "%d", group_id);
+  }
+  if (next_op > 0) {
+    html_hidden(out_f, "next_op", "%d", next_op);
+  }
+  html_hidden(out_f, "op", "%d", SSERV_OP_USER_EDIT_REG_ACTION);
+  cl = " class=\"b0\"";
+  fprintf(out_f, "<table%s>\n", cl);
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>%d</td></tr>\n",
+          cl, "User ID", cl, other_user_id);
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>%s</td></tr>\n",
+          cl, "User login", cl, ARMOR(u->login));
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>%d</td></tr>\n",
+          cl, "Contest ID", cl, other_contest_id);
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>%s</td></tr>\n",
+          cl, "Contest name", cl, ARMOR(cnts->name));
+
+  r = reg->status;
+  if (r < 0 || r >= USERLIST_REG_LAST) r = USERLIST_REG_LAST;
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>%s</td></tr>",
+          cl, "Status", cl, reg_status_strs[r]);
+  s = no;
+  if ((reg->flags & USERLIST_UC_INVISIBLE)) s = yes;
+  fprintf(out_f, "<tr><td%s><b>%s</td></td><td%s>%s</td></tr>\n",
+          cl, "Invisible?", cl, s);
+  s = no;
+  if ((reg->flags & USERLIST_UC_BANNED)) s = yes;
+  fprintf(out_f, "<tr><td%s><b>%s</td></td><td%s>%s</td></tr>\n",
+          cl, "Banned?", cl, s);
+  s = no;
+  if ((reg->flags & USERLIST_UC_LOCKED)) s = yes;
+  fprintf(out_f, "<tr><td%s><b>%s</td></td><td%s>%s</td></tr>\n",
+          cl, "Locked?", cl, s);
+  s = no;
+  if ((reg->flags & USERLIST_UC_INCOMPLETE)) s = yes;
+  fprintf(out_f, "<tr><td%s><b>%s</td></td><td%s>%s</td></tr>\n",
+          cl, "Incomplete?", cl, s);
+  s = no;
+  if ((reg->flags & USERLIST_UC_DISQUALIFIED)) s = yes;
+  fprintf(out_f, "<tr><td%s><b>%s</td></td><td%s>%s</td></tr>\n",
+          cl, "Disqualified?", cl, s);
+
+
+  fprintf(out_f, "<tr><td%s>&nbsp;</td><td%s><input type=\"submit\" name=\"submit\" value=\"Confirm delete!\" /></td></tr>\n", cl, cl);
+  fprintf(out_f, "</table>\n");
+  fprintf(out_f, "</form>\n");
+
+  fprintf(out_f, "<p>%s[%s]</a></p>",
+          html_hyperref(hbuf, sizeof(hbuf), phr->session_id, phr->self_url,
+                        NULL, "action=%d&amp;op=%d&amp;other_user_id=%d&amp;other_contest_id=%d%s%s%s",
+                        SSERV_CMD_HTTP_REQUEST, SSERV_OP_USER_EDIT_REG_PAGE,
+                        other_user_id, other_contest_id, contest_id_str, group_id_str, next_op_str),
+          "Edit");
+
+do_footer:
+  ss_write_html_footer(out_f);
+
+cleanup:
+  userlist_free(&u->b); u = 0;
+  xfree(xml_text); xml_text = 0;
+  html_armor_free(&ab);
+  return retval;
+}
+
+int
+super_serve_op_USER_CREATE_ONE_PAGE(
+        FILE *log_f,
+        FILE *out_f,
+        struct super_http_request_info *phr)
+{
+  int retval = 0, row, i;
+  int contest_id = 0, group_id = 0, other_contest_id_2 = 0;
+  const struct contest_desc *cnts = 0;
+  unsigned char buf[1024], hbuf[1024];
+  const unsigned char *cl = 0;
+  const int *cnts_id_list = 0;
+  int cnts_id_count = 0;
+  struct html_armor_buffer ab = HTML_ARMOR_INITIALIZER;
+  const unsigned char *s;
+  opcap_t caps = 0;
+
+  ss_cgi_param_int_opt(phr, "contest_id", &contest_id, 0);
+  ss_cgi_param_int_opt(phr, "group_id", &group_id, 0);
+
+  if (contest_id != 0) {
+    if (contests_get(contest_id, &cnts) < 0 || !cnts) contest_id = 0;
+  }
+  if (group_id < 0) group_id = 0;
+
+  if (get_global_caps(phr, &caps) < 0 || opcaps_check(caps, OPCAP_CREATE_USER) < 0) {
+    FAIL(S_ERR_PERM_DENIED);
+  }
+
+  cnts_id_count = contests_get_list(&cnts_id_list);
+  if (cnts_id_count <= 0 || !cnts_id_list) {
+    cnts_id_count = 0;
+    cnts_id_list = 0;
+  }
+
+  snprintf(buf, sizeof(buf), "serve-control: %s, create a new user",
+           phr->html_name);
+  ss_write_html_header(out_f, phr, buf, 0, NULL);
+
+  fprintf(out_f, "<script language=\"javascript\">\n");
+  fprintf(out_f,
+          "function changeEmail(form_obj)\n"
+          "{\n"
+          "  if (form_obj.other_email.value != null && form_obj.other_email.value != \"\") {\n"
+          "    document.getElementById(\"SendEmailRow\").style.display = \"\";\n"
+          "    changeSendEmail(form_obj);\n"
+          "  } else {\n"
+          "    document.getElementById(\"SendEmailRow\").style.display = \"none\";\n"
+          "    document.getElementById(\"ConfirmEmailRow\").style.display = \"none\";\n"
+          "  }\n"
+          "}\n");
+  fprintf(out_f,
+          "function changeSendEmail(form_obj)\n"
+          "{\n"
+          "  if (form_obj.send_email.checked) {\n"
+          "    document.getElementById(\"ConfirmEmailRow\").style.display = \"\";\n"
+          "  } else {\n"
+          "    document.getElementById(\"ConfirmEmailRow\").style.display = \"none\";\n"
+          "  }\n"
+          "}\n");
+  fprintf(out_f,
+          "function randomChar()\n"
+          "{\n"
+          "  var str = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\";\n"
+          "  var ind = Math.floor(Math.random() * str.length);\n"
+          "  if (ind < 0 || ind >= str.length) ind = 0;\n"
+          "  return str.charAt(ind);\n"
+          "}\n"
+          "function randomString(length)\n"
+          "{\n"
+          "  var res = \"\";\n"
+          "  for (var i = 0; i < length; ++i) {\n"
+          "    res += randomChar();\n"
+          "  }\n"
+          "  return res;\n"
+          "}\n"
+          "function generateRandomRegPassword()\n"
+          "{\n"
+          "  form_obj = document.getElementById(\"CreateForm\");\n"
+          "  form_obj.reg_random.value = randomString(16);\n"
+          "}\n"
+          "function copyRandomRegPassword()\n"
+          "{\n"
+          "  form_obj = document.getElementById(\"CreateForm\");\n"
+          "  form_obj.reg_password1.value = form_obj.reg_random.value;\n"
+          "  form_obj.reg_password2.value = form_obj.reg_random.value;\n"
+          "}\n"
+          "function generateRandomCntsPassword()\n"
+          "{\n"
+          "  form_obj = document.getElementById(\"CreateForm\");\n"
+          "  form_obj.cnts_random.value = randomString(16);\n"
+          "}\n"
+          "function copyRandomCntsPassword()\n"
+          "{\n"
+          "  form_obj = document.getElementById(\"CreateForm\");\n"
+          "  form_obj.cnts_password1.value = form_obj.cnts_random.value;\n"
+          "  form_obj.cnts_password2.value = form_obj.cnts_random.value;\n"
+          "}\n"
+          "function copyRegPassword()\n"
+          "{\n"
+          "  form_obj = document.getElementById(\"CreateForm\");\n"
+          "  form_obj.cnts_random.value = form_obj.reg_random.value;\n"
+          "  form_obj.cnts_password1.value = form_obj.reg_password1.value;\n"
+          "  form_obj.cnts_password2.value = form_obj.reg_password2.value;\n"
+          "  form_obj.cnts_sha1.checked = form_obj.reg_sha1.checked;\n"
+          "}\n");
+  fprintf(out_f,
+          "function toggleRowsVisibility2(value, tid, rowclass1, rowclass2)\n"
+          "{\n"
+          "  var vis1 = \"\";\n"
+          "  var vis2 = \"\";\n"
+          "  if (value == true) {\n"
+          "    vis1 = \"none\";\n"
+          "  } else {\n"
+          "    vis2 = \"none\";\n"
+          "  }\n"
+          "  var tobj = document.getElementById(tid);\n"
+          "  if (tobj == null) {\n"
+          "    return;\n"
+          "  }\n"
+          "  var trows = tobj.rows;\n"
+          "  if (trows != null) {\n"
+          "    for (var row in trows) {\n"
+          "      if (trows[row].className == rowclass1) {\n"
+          "        trows[row].style.display = vis1;\n"
+          "      } else if (trows[row].className == rowclass2) {\n"
+          "        trows[row].style.display = vis2;\n"
+          "      }\n"
+          "    }\n"
+          "  }\n"
+          "}\n"
+          "function changeCntsRegCreate(obj)\n"
+          "{\n"
+          "  toggleRowsVisibility2(obj.checked, \"CreateUserTable\", \"CntsRegRow0\", \"CntsRegRow\");\n"
+          "  updateCntsPasswdVisibility();\n"
+          "}\n"
+          "function changeGroupCreate(obj)\n"
+          "{\n"
+          "  toggleRowsVisibility2(obj.checked, \"CreateUserTable\", \"GroupRow0\", \"GroupRow\");\n"
+          "}\n"
+          "");
+  fprintf(out_f,
+          "function updateCnts1()\n"
+          "{\n"
+          "  var obj1 = document.getElementById(\"cnts1\");\n"
+          "  var obj2 = document.getElementById(\"cnts2\");\n"
+          "  var value = obj1.value;\n"
+          "  var i;\n"
+          "  for (i = 0; i < obj2.options.length; ++i) {\n"
+          "    if (obj2.options[i].value == value) {\n"
+          "      obj2.options.selectedIndex = i;\n"
+          "      break;\n"
+          "    }\n"
+          "  }\n"
+          "  updateCntsPasswdVisibility();\n"
+          "}\n");
+  fprintf(out_f,
+          "function updateCnts2()\n"
+          "{\n"
+          "  var obj1 = document.getElementById(\"cnts1\");\n"
+          "  var obj2 = document.getElementById(\"cnts2\");\n"
+          "  var value = obj2.options[obj2.selectedIndex].value;\n"
+          "  obj1.value = value;\n"
+          "  updateCntsPasswdVisibility();\n"
+          "}\n");
+  fprintf(out_f, "var cnts_passwd_enabled = { ");
+  row = 0;
+  for (i = 0; i < cnts_id_count; ++i) {
+    other_contest_id_2 = cnts_id_list[i];
+    if (other_contest_id_2 <= 0) continue;
+    if (contests_get(other_contest_id_2, &cnts) < 0 || !cnts) continue;
+    if (!cnts->disable_team_password) {
+      if (row) fprintf(out_f, ", ");
+      ++row;
+      fprintf(out_f, "%d : true", other_contest_id_2);
+    }
+  }
+  fprintf(out_f, "};\n");
+  // CntsRegRowUseRegPasswd cnts_use_reg_passwd
+  // CntsRegRowSetToNull    cnts_rull_passwd
+  // CntsRegRowPasswd1      cnts_password1
+  // CntsRegRowPasswd2      cnts_password2
+  // CntsRegRowPasswdRandom cnts_random
+  // CntsRegRowPasswdSha1   cnts_sha1
+  fprintf(out_f,
+          "function updateCntsPasswdVisibility()\n"
+          "{\n"
+          "  form_obj = document.getElementById(\"CreateForm\");\n"
+          "  if (!form_obj.reg_cnts_create.checked || !cnts_passwd_enabled[form_obj.other_contest_id_1.value]) {\n"
+          "    form_obj.cnts_use_reg_passwd.checked = false;\n"
+          "    form_obj.cnts_null_passwd.checked = false;\n"
+          "    form_obj.cnts_password1.value = \"\";\n"
+          "    form_obj.cnts_password2.value = \"\";\n"
+          "    form_obj.cnts_random.value = \"\";\n"
+          "    form_obj.cnts_sha1.checked = false;\n"
+          "    document.getElementById(\"CntsRegRowUseRegPasswd\").style.display = \"none\";\n"
+          "    document.getElementById(\"CntsRegRowSetToNull\").style.display = \"none\";\n"
+          "    document.getElementById(\"CntsRegRowPasswd1\").style.display = \"none\";\n"
+          "    document.getElementById(\"CntsRegRowPasswd2\").style.display = \"none\";\n"
+          "    document.getElementById(\"CntsRegRowPasswdRandom\").style.display = \"none\";\n"
+          "    document.getElementById(\"CntsRegRowPasswdSha1\").style.display = \"none\";\n"
+          "  } else {\n"
+          "    document.getElementById(\"CntsRegRowUseRegPasswd\").style.display = \"\";\n"
+          "    if (form_obj.cnts_use_reg_passwd.checked) {\n"
+          "      form_obj.cnts_null_passwd.checked = false;\n"
+          "      form_obj.cnts_password1.value = \"\";\n"
+          "      form_obj.cnts_password2.value = \"\";\n"
+          "      form_obj.cnts_random.value = \"\";\n"
+          "      form_obj.cnts_sha1.checked = false;\n"
+          "      document.getElementById(\"CntsRegRowSetToNull\").style.display = \"none\";\n"
+          "      document.getElementById(\"CntsRegRowPasswd1\").style.display = \"none\";\n"
+          "      document.getElementById(\"CntsRegRowPasswd2\").style.display = \"none\";\n"
+          "      document.getElementById(\"CntsRegRowPasswdRandom\").style.display = \"none\";\n"
+          "      document.getElementById(\"CntsRegRowPasswdSha1\").style.display = \"none\";\n"
+          "    } else {\n"
+          "      document.getElementById(\"CntsRegRowSetToNull\").style.display = \"\";\n"
+          "      if (form_obj.cnts_null_passwd.checked) {\n"
+          "        form_obj.cnts_password1.value = \"\";\n"
+          "        form_obj.cnts_password2.value = \"\";\n"
+          "        form_obj.cnts_random.value = \"\";\n"
+          "        form_obj.cnts_sha1.checked = false;\n"
+          "        document.getElementById(\"CntsRegRowPasswd1\").style.display = \"none\";\n"
+          "        document.getElementById(\"CntsRegRowPasswd2\").style.display = \"none\";\n"
+          "        document.getElementById(\"CntsRegRowPasswdRandom\").style.display = \"none\";\n"
+          "        document.getElementById(\"CntsRegRowPasswdSha1\").style.display = \"none\";\n"
+          "      } else {\n"
+          "        document.getElementById(\"CntsRegRowPasswd1\").style.display = \"\";\n"
+          "        document.getElementById(\"CntsRegRowPasswd2\").style.display = \"\";\n"
+          "        document.getElementById(\"CntsRegRowPasswdRandom\").style.display = \"\";\n"
+          "        document.getElementById(\"CntsRegRowPasswdSha1\").style.display = \"\";\n"
+          "      }\n"
+          "    }\n"
+          "  }\n"
+          "}\n");
+  fprintf(out_f, "</script>\n");
+
+  fprintf(out_f, "<h1>%s</h1>\n<br/>\n", buf);
+
+  print_top_navigation_links(log_f, out_f, phr, contest_id, group_id, 0, NULL);
+
+  html_start_form_id(out_f, 1, phr->self_url, "CreateForm", "");
+  html_hidden(out_f, "SID", "%016llx", phr->session_id);
+  html_hidden(out_f, "action", "%d", SSERV_CMD_HTTP_REQUEST);
+  html_hidden(out_f, "op", "%d", SSERV_OP_USER_CREATE_ONE_ACTION);
+  if (contest_id > 0) {
+    html_hidden(out_f, "contest_id", "%d", contest_id);
+  }
+  if (group_id > 0) {
+    html_hidden(out_f, "group_id", "%d", group_id);
+  }
+  cl = " class=\"b0\"";
+  fprintf(out_f, "<table%s id=\"CreateUserTable\">\n", cl);
+  fprintf(out_f, "<tr><td%s><b>%s*:</b></td><td%s><input type=\"text\" size=\"40\" name=\"other_login\" /></td><td%s>&nbsp;</td></tr>\n",
+          cl, "Login", cl, cl);
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s><input type=\"text\" size=\"40\" onchange=\"changeEmail(this.form)\" name=\"other_email\" /></td><td%s>&nbsp;</td></tr>\n",
+          cl, "E-mail", cl, cl);
+  fprintf(out_f, "<tr id=\"SendEmailRow\" style=\"display: none;\"><td%s><b>%s:</b></td><td%s><input type=\"checkbox\" onchange=\"changeSendEmail(this.form)\" name=\"send_email\" value=\"1\" /></td><td%s>&nbsp;</td></tr>\n",
+          cl, "Send registration e-mail", cl, cl);
+  fprintf(out_f, "<tr id=\"ConfirmEmailRow\" style=\"display: none;\"><td%s><b>%s:</b></td><td%s><input type=\"checkbox\" name=\"confirm_email\" value=\"1\" /></td><td%s>&nbsp;</td></tr>\n",
+          cl, "Confirm e-mail by user", cl, cl);
+  fprintf(out_f, "<tr><td%s><b>%s*:</b></td><td%s><input type=\"password\" name=\"reg_password1\" size=\"40\" /></td><td%s>&nbsp;</td></tr>\n",
+          cl, "Registration password", cl, cl);
+  fprintf(out_f, "<tr><td%s><b>%s*:</b></td><td%s><input type=\"password\" name=\"reg_password2\" size=\"40\" /></td><td%s>&nbsp;</td></tr>\n",
+          cl, "Confirm password", cl, cl);
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s><input type=\"text\" name=\"reg_random\" size=\"40\" /></td><td%s><a onclick=\"generateRandomRegPassword()\">[%s]</a>&nbsp;<a onclick=\"copyRandomRegPassword()\">[%s]</a></td></tr>\n",
+          cl, "Random password", cl, cl, "Generate", "Copy");
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s><input type=\"checkbox\" name=\"reg_sha1\" value=\"1\" /></td><td%s>&nbsp;</td></tr>\n",
+          cl, "Use SHA1", cl, cl);
+
+  for (row = 0; user_flag_rows[row].field_id > 0; ++row) {
+    fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s><input type=\"checkbox\" name=\"field_%d\" value=\"1\" /></td><td%s>&nbsp;</td></tr>\n",
+            cl, user_flag_rows[row].field_desc, cl, user_flag_rows[row].field_id, cl);
+  }
+
+  fprintf(out_f, "<tr><td%s colspan=\"3\" align=\"center\"><b>%s</b></td></tr>\n",
+          cl, "Contest registration");
+
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s><input type=\"checkbox\" onchange=\"changeCntsRegCreate(this)\" name=\"reg_cnts_create\" value=\"1\" /></td><td%s>&nbsp;</td></tr>\n",
+          cl, "Create a contest registration", cl, cl);
+
+  hbuf[0] = 0;
+  if (contest_id > 0) {
+    snprintf(hbuf, sizeof(hbuf), "%d", contest_id);
+  }
+  fprintf(out_f, "<tr class=\"CntsRegRow\" style=\"display: none;\" ><td%s><b>%s:</b></td><td%s><input id=\"cnts1\" onchange=\"updateCnts1()\" type=\"text\" name=\"other_contest_id_1\" size=\"20\" value=\"%s\"/></td><td%s>&nbsp;</td></tr>\n",
+          cl, "Contest ID", cl, hbuf, cl);
+  if (cnts_id_count > 0) {
+    fprintf(out_f, "<tr class=\"CntsRegRow\" style=\"display: none;\" ><td%s><b>%s:</b></td><td%s>", cl, "Contest name", cl);
+    fprintf(out_f, "<select id=\"cnts2\" onchange=\"updateCnts2()\" name=\"other_contest_id_2\"><option value=\"0\"></option>");
+    for (i = 0; i < cnts_id_count; ++i) {
+      other_contest_id_2 = cnts_id_list[i];
+      if (other_contest_id_2 <= 0) continue;
+      if (contests_get(other_contest_id_2, &cnts) < 0 || !cnts) continue;
+      if (cnts->closed) continue;
+      s = "";
+      if (contest_id > 0 && cnts->id == contest_id) {
+        s = " selected=\"selected\"";
+      }
+      fprintf(out_f, "<option value=\"%d\"%s>%s</option>", other_contest_id_2, s, ARMOR(cnts->name));
+    }
+    fprintf(out_f, "</select>");
+    fprintf(out_f, "</td><td%s>&nbsp;</td></tr>\n", cl);
+  }
+  fprintf(out_f, "<tr class=\"CntsRegRow\" style=\"display: none;\" ><td%s><b>%s:</b></td><td%s>", cl, "Status", cl);
+  ss_select(out_f, "cnts_status", (const unsigned char* []) { "OK", "Pending", "Rejected", NULL }, 1);
+  fprintf(out_f, "</td></tr>\n");
+  fprintf(out_f, "<tr class=\"CntsRegRow\" style=\"display: none;\" ><td%s><b>%s</td></td><td%s><input type=\"checkbox\" value=\"1\" name=\"%s\" /></td></tr>\n",
+          cl, "Invisible?", cl, "is_invisible");
+  fprintf(out_f, "<tr class=\"CntsRegRow\" style=\"display: none;\" ><td%s><b>%s</td></td><td%s><input type=\"checkbox\" value=\"1\" name=\"%s\" /></td></tr>\n",
+          cl, "Banned?", cl, "is_banned");
+  fprintf(out_f, "<tr class=\"CntsRegRow\" style=\"display: none;\" ><td%s><b>%s</td></td><td%s><input type=\"checkbox\" value=\"1\" name=\"%s\" /></td></tr>\n",
+          cl, "Locked?", cl, "is_locked");
+  fprintf(out_f, "<tr class=\"CntsRegRow\" style=\"display: none;\" ><td%s><b>%s</td></td><td%s><input type=\"checkbox\" value=\"1\" name=\"%s\" /></td></tr>\n",
+          cl, "Incomplete?", cl, "is_incomplete");
+  fprintf(out_f, "<tr class=\"CntsRegRow\" style=\"display: none;\" ><td%s><b>%s</td></td><td%s><input type=\"checkbox\" value=\"1\" name=\"%s\" /></td></tr>\n",
+          cl, "Disqualified?", cl, "is_disqualified");
+
+  fprintf(out_f, "<tr class=\"CntsRegRow\" id=\"CntsRegRowUseRegPasswd\" style=\"display: none;\" ><td%s><b>%s</td></td><td%s><input type=\"checkbox\" value=\"1\" onchange=\"updateCntsPasswdVisibility()\" name=\"%s\" /></td></tr>\n",
+          cl, "Use reg. password?", cl, "cnts_use_reg_passwd");
+  fprintf(out_f, "<tr class=\"CntsRegRow\" id=\"CntsRegRowSetToNull\" style=\"display: none;\" ><td%s><b>%s</td></td><td%s><input type=\"checkbox\" value=\"1\" onchange=\"updateCntsPasswdVisibility()\" name=\"%s\" /></td></tr>\n",
+          cl, "Set to null?", cl, "cnts_null_passwd");
+  fprintf(out_f, "<tr class=\"CntsRegRow\" id=\"CntsRegRowPasswd1\" style=\"display: none;\" ><td%s><b>%s:</b></td><td%s><input type=\"password\" name=\"cnts_password1\" size=\"40\" /></td><td%s>&nbsp;</td></tr>\n",
+          cl, "Contest password", cl, cl);
+  fprintf(out_f, "<tr class=\"CntsRegRow\" id=\"CntsRegRowPasswd2\" style=\"display: none;\" ><td%s><b>%s:</b></td><td%s><input type=\"password\" name=\"cnts_password2\" size=\"40\" /></td><td%s>&nbsp;</td></tr>\n",
+          cl, "Confirm password", cl, cl);
+  fprintf(out_f, "<tr class=\"CntsRegRow\" id=\"CntsRegRowPasswdRandom\" style=\"display: none;\" ><td%s><b>%s:</b></td><td%s><input type=\"text\" name=\"cnts_random\" size=\"40\" /></td><td%s><a onclick=\"generateRandomCntsPassword()\">[%s]</a>&nbsp;<a onclick=\"copyRandomCntsPassword()\">[%s]</a>&nbsp;<a onclick=\"copyRegPassword()\">[%s]</a></td></tr>\n",
+          cl, "Random password", cl, cl, "Generate", "Copy", "Copy reg. password");
+  fprintf(out_f, "<tr class=\"CntsRegRow\" id=\"CntsRegRowPasswdSha1\" style=\"display: none;\" ><td%s><b>%s:</b></td><td%s><input type=\"checkbox\" name=\"cnts_sha1\" value=\"1\" /></td><td%s>&nbsp;</td></tr>\n",
+          cl, "Use SHA1", cl, cl);
+
+  fprintf(out_f, "<tr class=\"CntsRegRow\" style=\"display: none;\" ><td%s><b>%s:</b></td><td%s><input type=\"text\" size=\"40\" name=\"cnts_name\" /></td><td%s>&nbsp;</td></tr>\n",
+          cl, "User name", cl, cl);
+
+  fprintf(out_f, "<tr><td%s colspan=\"3\" align=\"center\"><b>%s</b></td></tr>\n",
+          cl, "Group membership");
+
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s><input type=\"checkbox\" onchange=\"changeGroupCreate(this)\" name=\"group_create\" value=\"1\" /></td><td%s>&nbsp;</td></tr>\n",
+          cl, "Add user to a group", cl, cl);
+  hbuf[0] = 0;
+  if (group_id > 0) {
+    snprintf(hbuf, sizeof(hbuf), "%d", group_id);
+  }
+  fprintf(out_f, "<tr class=\"GroupRow\" style=\"display: none;\" ><td%s><b>%s:</b></td><td%s><input type=\"text\" name=\"other_group_id\" size=\"20\" value=\"%s\"/></td><td%s>&nbsp;</td></tr>\n",
+          cl, "Group ID", cl, hbuf, cl);
+
+  fprintf(out_f, "<tr><td%s>&nbsp;</td><td%s><input type=\"submit\" name=\"submit\" value=\"%s\" /></td><td%s>&nbsp;</td></tr>\n",
+          cl, cl, "Create a user", cl);
+  fprintf(out_f, "</table>\n");
+  fprintf(out_f, "</form>\n");
+
+  ss_write_html_footer(out_f);
+
+cleanup:
+  html_armor_free(&ab);
+  return retval;
+}
+
+int
+super_serve_op_USER_CREATE_MANY_PAGE(
+        FILE *log_f,
+        FILE *out_f,
+        struct super_http_request_info *phr)
+{
+  int retval = 0, row, i;
+  int contest_id = 0, group_id = 0, other_contest_id_2 = 0;
+  const struct contest_desc *cnts = 0;
+  unsigned char buf[1024], hbuf[1024];
+  const unsigned char *cl = 0;
+  const int *cnts_id_list = 0;
+  int cnts_id_count = 0;
+  struct html_armor_buffer ab = HTML_ARMOR_INITIALIZER;
+  const unsigned char *s;
+  opcap_t caps = 0;
+
+  ss_cgi_param_int_opt(phr, "contest_id", &contest_id, 0);
+  ss_cgi_param_int_opt(phr, "group_id", &group_id, 0);
+
+  if (contest_id != 0) {
+    if (contests_get(contest_id, &cnts) < 0 || !cnts) contest_id = 0;
+  }
+  if (group_id < 0) group_id = 0;
+
+  if (get_global_caps(phr, &caps) < 0 || opcaps_check(caps, OPCAP_CREATE_USER) < 0) {
+    FAIL(S_ERR_PERM_DENIED);
+  }
+
+  cnts_id_count = contests_get_list(&cnts_id_list);
+  if (cnts_id_count <= 0 || !cnts_id_list) {
+    cnts_id_count = 0;
+    cnts_id_list = 0;
+  }
+
+  snprintf(buf, sizeof(buf), "serve-control: %s, create many new users",
+           phr->html_name);
+  ss_write_html_header(out_f, phr, buf, 0, NULL);
+
+  fprintf(out_f, "<script language=\"javascript\" src=\"%ssprintf.js\" ></script>\n",
+          CONF_STYLE_PREFIX);
+  fprintf(out_f, "<script language=\"javascript\">\n");
+  fprintf(out_f,
+          "function changeEmail(form_obj)\n"
+          "{\n"
+          "  if (form_obj.other_email.value != null && form_obj.other_email.value != \"\") {\n"
+          "    document.getElementById(\"SendEmailRow\").style.display = \"\";\n"
+          "    changeSendEmail(form_obj);\n"
+          "  } else {\n"
+          "    document.getElementById(\"SendEmailRow\").style.display = \"none\";\n"
+          "    document.getElementById(\"ConfirmEmailRow\").style.display = \"none\";\n"
+          "  }\n"
+          "}\n");
+  fprintf(out_f,
+          "function changeSendEmail(form_obj)\n"
+          "{\n"
+          "  if (form_obj.send_email.checked) {\n"
+          "    document.getElementById(\"ConfirmEmailRow\").style.display = \"\";\n"
+          "  } else {\n"
+          "    document.getElementById(\"ConfirmEmailRow\").style.display = \"none\";\n"
+          "  }\n"
+          "}\n");
+  fprintf(out_f,
+          "function randomChar()\n"
+          "{\n"
+          "  var str = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\";\n"
+          "  var ind = Math.floor(Math.random() * str.length);\n"
+          "  if (ind < 0 || ind >= str.length) ind = 0;\n"
+          "  return str.charAt(ind);\n"
+          "}\n"
+          "function randomString(length)\n"
+          "{\n"
+          "  var res = \"\";\n"
+          "  for (var i = 0; i < length; ++i) {\n"
+          "    res += randomChar();\n"
+          "  }\n"
+          "  return res;\n"
+          "}\n"
+          "function generateRandomRegPassword()\n"
+          "{\n"
+          "  form_obj = document.getElementById(\"CreateForm\");\n"
+          "  form_obj.reg_random.value = randomString(16);\n"
+          "}\n"
+          "function copyRandomRegPassword()\n"
+          "{\n"
+          "  form_obj = document.getElementById(\"CreateForm\");\n"
+          "  form_obj.reg_password1.value = form_obj.reg_random.value;\n"
+          "  form_obj.reg_password2.value = form_obj.reg_random.value;\n"
+          "}\n"
+          "function generateRandomCntsPassword()\n"
+          "{\n"
+          "  form_obj = document.getElementById(\"CreateForm\");\n"
+          "  form_obj.cnts_random.value = randomString(16);\n"
+          "}\n"
+          "function copyRandomCntsPassword()\n"
+          "{\n"
+          "  form_obj = document.getElementById(\"CreateForm\");\n"
+          "  form_obj.cnts_password1.value = form_obj.cnts_random.value;\n"
+          "  form_obj.cnts_password2.value = form_obj.cnts_random.value;\n"
+          "}\n"
+          "function copyRegPassword()\n"
+          "{\n"
+          "  form_obj = document.getElementById(\"CreateForm\");\n"
+          "  form_obj.cnts_random.value = form_obj.reg_random.value;\n"
+          "  form_obj.cnts_password1.value = form_obj.reg_password1.value;\n"
+          "  form_obj.cnts_password2.value = form_obj.reg_password2.value;\n"
+          "  form_obj.cnts_sha1.checked = form_obj.reg_sha1.checked;\n"
+          "}\n");
+  fprintf(out_f,
+          "function toggleRowsVisibility2(value, tid, rowclass1, rowclass2)\n"
+          "{\n"
+          "  var vis1 = \"\";\n"
+          "  var vis2 = \"\";\n"
+          "  if (value == true) {\n"
+          "    vis1 = \"none\";\n"
+          "  } else {\n"
+          "    vis2 = \"none\";\n"
+          "  }\n"
+          "  var tobj = document.getElementById(tid);\n"
+          "  if (tobj == null) {\n"
+          "    return;\n"
+          "  }\n"
+          "  var trows = tobj.rows;\n"
+          "  if (trows != null) {\n"
+          "    for (var row in trows) {\n"
+          "      if (trows[row].className == rowclass1) {\n"
+          "        trows[row].style.display = vis1;\n"
+          "      } else if (trows[row].className == rowclass2) {\n"
+          "        trows[row].style.display = vis2;\n"
+          "      }\n"
+          "    }\n"
+          "  }\n"
+          "}\n"
+          "function changeCntsRegCreate(obj)\n"
+          "{\n"
+          "  toggleRowsVisibility2(obj.checked, \"CreateUserTable\", \"CntsRegRow0\", \"CntsRegRow\");\n"
+          "  updateCntsPasswdVisibility();\n"
+          "}\n"
+          "function changeGroupCreate(obj)\n"
+          "{\n"
+          "  toggleRowsVisibility2(obj.checked, \"CreateUserTable\", \"GroupRow0\", \"GroupRow\");\n"
+          "}\n"
+          "");
+  fprintf(out_f,
+          "function updateCnts1()\n"
+          "{\n"
+          "  var obj1 = document.getElementById(\"cnts1\");\n"
+          "  var obj2 = document.getElementById(\"cnts2\");\n"
+          "  var value = obj1.value;\n"
+          "  var i;\n"
+          "  for (i = 0; i < obj2.options.length; ++i) {\n"
+          "    if (obj2.options[i].value == value) {\n"
+          "      obj2.options.selectedIndex = i;\n"
+          "      break;\n"
+          "    }\n"
+          "  }\n"
+          "  updateCntsPasswdVisibility();\n"
+          "}\n");
+  fprintf(out_f,
+          "function updateCnts2()\n"
+          "{\n"
+          "  var obj1 = document.getElementById(\"cnts1\");\n"
+          "  var obj2 = document.getElementById(\"cnts2\");\n"
+          "  var value = obj2.options[obj2.selectedIndex].value;\n"
+          "  obj1.value = value;\n"
+          "  updateCntsPasswdVisibility();\n"
+          "}\n");
+  fprintf(out_f, "var cnts_passwd_enabled = { ");
+  row = 0;
+  for (i = 0; i < cnts_id_count; ++i) {
+    other_contest_id_2 = cnts_id_list[i];
+    if (other_contest_id_2 <= 0) continue;
+    if (contests_get(other_contest_id_2, &cnts) < 0 || !cnts) continue;
+    if (!cnts->disable_team_password) {
+      if (row) fprintf(out_f, ", ");
+      ++row;
+      fprintf(out_f, "%d : true", other_contest_id_2);
+    }
+  }
+  fprintf(out_f, "};\n");
+  // CntsRegRowUseRegPasswd   cnts_use_reg_passwd
+  // CntsRegRowSetToNull      cnts_null_passwd
+  // CntsRegRowUseRandom      cnts_random_passwd
+  // CntsRegRowPasswdTemplate cnts_password_template
+  // CntsRegRowPasswdSha1     cnts_sha1
+  fprintf(out_f,
+          "function updateCntsPasswdVisibility()\n"
+          "{\n"
+          "  form_obj = document.getElementById(\"CreateForm\");\n"
+          "  if (!form_obj.reg_cnts_create.checked || !cnts_passwd_enabled[form_obj.other_contest_id_1.value]) {\n"
+          "    form_obj.cnts_use_reg_passwd.checked = false;\n"
+          "    form_obj.cnts_null_passwd.checked = false;\n"
+          "    form_obj.cnts_random_passwd.checked = false;\n"
+          "    form_obj.cnts_password_template.value = \"\";\n"
+          "    form_obj.cnts_sha1.checked = false;\n"
+          "    document.getElementById(\"CntsRegRowUseRegPasswd\").style.display = \"none\";\n"
+          "    document.getElementById(\"CntsRegRowSetToNull\").style.display = \"none\";\n"
+          "    document.getElementById(\"CntsRegRowUseRandom\").style.display = \"none\";\n"
+          "    document.getElementById(\"CntsRegRowPasswdTemplate\").style.display = \"none\";\n"
+          "    document.getElementById(\"CntsRegRowPasswdSha1\").style.display = \"none\";\n"
+          "  } else {\n"
+          "    document.getElementById(\"CntsRegRowUseRegPasswd\").style.display = \"\";\n"
+          "    if (form_obj.cnts_use_reg_passwd.checked) {\n"
+          "      form_obj.cnts_null_passwd.checked = false;\n"
+          "      form_obj.cnts_random_passwd.checked = false;\n"
+          "      form_obj.cnts_password_template.value = \"\";\n"
+          "      form_obj.cnts_sha1.checked = false;\n"
+          "      document.getElementById(\"CntsRegRowSetToNull\").style.display = \"none\";\n"
+          "      document.getElementById(\"CntsRegRowUseRandom\").style.display = \"none\";\n"
+          "      document.getElementById(\"CntsRegRowPasswdTemplate\").style.display = \"none\";\n"
+          "      document.getElementById(\"CntsRegRowPasswdSha1\").style.display = \"none\";\n"
+          "    } else {\n"
+          "      document.getElementById(\"CntsRegRowSetToNull\").style.display = \"\";\n"
+          "      if (form_obj.cnts_null_passwd.checked) {\n"
+          "        form_obj.cnts_random_passwd.checked = false;\n"
+          "        form_obj.cnts_password_template.value = \"\";\n"
+          "        form_obj.cnts_sha1.checked = false;\n"
+          "        document.getElementById(\"CntsRegRowUseRandom\").style.display = \"none\";\n"
+          "        document.getElementById(\"CntsRegRowPasswdTemplate\").style.display = \"none\";\n"
+          "        document.getElementById(\"CntsRegRowPasswdSha1\").style.display = \"none\";\n"
+          "      } else {\n"
+          "        document.getElementById(\"CntsRegRowUseRandom\").style.display = \"\";\n"
+          "        if (form_obj.cnts_random_passwd.checked) {\n"
+          "          form_obj.cnts_password_template.value = \"\";\n"
+          "          form_obj.cnts_sha1.checked = false;\n"
+          "          document.getElementById(\"CntsRegRowPasswdTemplate\").style.display = \"none\";\n"
+          "          document.getElementById(\"CntsRegRowPasswdSha1\").style.display = \"none\";\n"
+          "        } else {\n"
+          "          document.getElementById(\"CntsRegRowPasswdTemplate\").style.display = \"\";\n"
+          "          document.getElementById(\"CntsRegRowPasswdSha1\").style.display = \"\";\n"
+          "        }\n"
+          "      }\n"
+          "    }\n"
+          "  }\n"
+          "}\n");
+  fprintf(out_f,
+          "function formatLogins()\n"
+          "{\n"
+          "  var form_obj = document.getElementById(\"CreateForm\");\n"
+          "  var div_obj = document.getElementById(\"LoginsCreated\");\n"
+          "  if (div_obj.childNodes.length == 1) {\n"
+          "    div_obj.removeChild(div_obj.childNodes[0]);\n"
+          "  }\n"
+          "  var str = \"\";\n"
+          "  var first = parseInt(form_obj.first_serial.value);\n"
+          "  var last = parseInt(form_obj.last_serial.value);\n"
+          "  var format = form_obj.login_template.value;\n"
+          "  if (first != null && first != NaN && last != null && last != NaN && first >= 0 && last >= 0 && first <= last && last - first + 1 <= 10000 && format != null && format.length > 0) {\n"
+          "    if (last - first + 1 <= 5) {\n"
+          "      for (var i = first; i <= last; ++i) {\n"
+          "        str += \" \" + sprintf(format, i);\n"
+          "      }\n"
+          "    } else {\n"
+          "      str += sprintf(format, first);\n"
+          "      str += \" \" + sprintf(format, first + 1);\n"
+          "      str += \" \" + sprintf(format, first + 2);\n"
+          "      str += \" ...\";\n"
+          "      str += \" \" + sprintf(format, last - 1);\n"
+          "      str += \" \" + sprintf(format, last);\n"
+          "    }\n"
+          "  }\n"
+          "  var node = document.createTextNode(str);\n"
+          "  div_obj.appendChild(node);\n"
+          "}\n"
+          "");
+  fprintf(out_f, "</script>\n");
+
+  fprintf(out_f, "<h1>%s</h1>\n<br/>\n", buf);
+
+  print_top_navigation_links(log_f, out_f, phr, contest_id, group_id, 0, NULL);
+
+  html_start_form_id(out_f, 1, phr->self_url, "CreateForm", "");
+  html_hidden(out_f, "SID", "%016llx", phr->session_id);
+  html_hidden(out_f, "action", "%d", SSERV_CMD_HTTP_REQUEST);
+  html_hidden(out_f, "op", "%d", SSERV_OP_USER_CREATE_MANY_ACTION);
+  if (contest_id > 0) {
+    html_hidden(out_f, "contest_id", "%d", contest_id);
+  }
+  if (group_id > 0) {
+    html_hidden(out_f, "group_id", "%d", group_id);
+  }
+  cl = " class=\"b0\"";
+  fprintf(out_f, "<table%s id=\"CreateUserTable\">\n", cl);
+  fprintf(out_f, "<tr><td%s><b>%s*:</b></td><td%s><input type=\"text\" size=\"40\" name=\"first_serial\" onchange=\"formatLogins()\" /></td><td%s>&nbsp;</td></tr>\n",
+          cl, "First serial number", cl, cl);
+  fprintf(out_f, "<tr><td%s><b>%s*:</b></td><td%s><input type=\"text\" size=\"40\" name=\"last_serial\" onchange=\"formatLogins()\" /></td><td%s>&nbsp;</td></tr>\n",
+          cl, "Last serial number", cl, cl);
+  fprintf(out_f, "<tr><td%s><b>%s*:</b></td><td%s><input type=\"text\" size=\"40\" name=\"login_template\" onchange=\"formatLogins()\" /></td><td%s>&nbsp;</td></tr>\n",
+          cl, "Login template", cl, cl);
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s colspan=\"2\"><div id=\"LoginsCreated\" style=\"display: inline;\"></div></td></tr>\n",
+          cl, "Logins to be created", cl);
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s><input type=\"checkbox\" onchange=\"changeRandomRegPassword()\" name=\"reg_random\" value=\"1\" /></td><td%s>&nbsp;</td></tr>\n",
+          cl, "Use random password", cl, cl);
+  fprintf(out_f, "<tr id=\"RegPasswordTemplateRow\"><td%s><b>%s:</b></td><td%s><input type=\"text\" size=\"40\" name=\"reg_password_template\" /></td><td%s>&nbsp;</td></tr>\n",
+          cl, "Password template", cl, cl);
+  fprintf(out_f, "<tr id=\"RegPasswordSha1Row\"><td%s><b>%s:</b></td><td%s><input type=\"checkbox\" name=\"reg_sha1\" value=\"1\" /></td><td%s>&nbsp;</td></tr>\n",
+          cl, "Use SHA1", cl, cl);
+
+  for (row = 0; user_flag_rows[row].field_id > 0; ++row) {
+    fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s><input type=\"checkbox\" name=\"field_%d\" value=\"1\" /></td><td%s>&nbsp;</td></tr>\n",
+            cl, user_flag_rows[row].field_desc, cl, user_flag_rows[row].field_id, cl);
+  }
+
+  fprintf(out_f, "<tr><td%s colspan=\"3\" align=\"center\"><b>%s</b></td></tr>\n",
+          cl, "Contest registration");
+
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s><input type=\"checkbox\" onchange=\"changeCntsRegCreate(this)\" name=\"reg_cnts_create\" value=\"1\" /></td><td%s>&nbsp;</td></tr>\n",
+          cl, "Create a contest registration", cl, cl);
+
+  hbuf[0] = 0;
+  if (contest_id > 0) {
+    snprintf(hbuf, sizeof(hbuf), "%d", contest_id);
+  }
+  fprintf(out_f, "<tr class=\"CntsRegRow\" style=\"display: none;\" ><td%s><b>%s:</b></td><td%s><input id=\"cnts1\" onchange=\"updateCnts1()\" type=\"text\" name=\"other_contest_id_1\" size=\"20\" value=\"%s\"/></td><td%s>&nbsp;</td></tr>\n",
+          cl, "Contest ID", cl, hbuf, cl);
+  if (cnts_id_count > 0) {
+    fprintf(out_f, "<tr class=\"CntsRegRow\" style=\"display: none;\" ><td%s><b>%s:</b></td><td%s>", cl, "Contest name", cl);
+    fprintf(out_f, "<select id=\"cnts2\" onchange=\"updateCnts2()\" name=\"other_contest_id_2\"><option value=\"0\"></option>");
+    for (i = 0; i < cnts_id_count; ++i) {
+      other_contest_id_2 = cnts_id_list[i];
+      if (other_contest_id_2 <= 0) continue;
+      if (contests_get(other_contest_id_2, &cnts) < 0 || !cnts) continue;
+      if (cnts->closed) continue;
+      s = "";
+      if (contest_id > 0 && cnts->id == contest_id) {
+        s = " selected=\"selected\"";
+      }
+      fprintf(out_f, "<option value=\"%d\"%s>%s</option>", other_contest_id_2, s, ARMOR(cnts->name));
+    }
+    fprintf(out_f, "</select>");
+    fprintf(out_f, "</td><td%s>&nbsp;</td></tr>\n", cl);
+  }
+  fprintf(out_f, "<tr class=\"CntsRegRow\" style=\"display: none;\" ><td%s><b>%s:</b></td><td%s>", cl, "Status", cl);
+  ss_select(out_f, "cnts_status", (const unsigned char* []) { "OK", "Pending", "Rejected", NULL }, 1);
+  fprintf(out_f, "</td></tr>\n");
+  fprintf(out_f, "<tr class=\"CntsRegRow\" style=\"display: none;\" ><td%s><b>%s</td></td><td%s><input type=\"checkbox\" value=\"1\" name=\"%s\" /></td></tr>\n",
+          cl, "Invisible?", cl, "is_invisible");
+  fprintf(out_f, "<tr class=\"CntsRegRow\" style=\"display: none;\" ><td%s><b>%s</td></td><td%s><input type=\"checkbox\" value=\"1\" name=\"%s\" /></td></tr>\n",
+          cl, "Banned?", cl, "is_banned");
+  fprintf(out_f, "<tr class=\"CntsRegRow\" style=\"display: none;\" ><td%s><b>%s</td></td><td%s><input type=\"checkbox\" value=\"1\" name=\"%s\" /></td></tr>\n",
+          cl, "Locked?", cl, "is_locked");
+  fprintf(out_f, "<tr class=\"CntsRegRow\" style=\"display: none;\" ><td%s><b>%s</td></td><td%s><input type=\"checkbox\" value=\"1\" name=\"%s\" /></td></tr>\n",
+          cl, "Incomplete?", cl, "is_incomplete");
+  fprintf(out_f, "<tr class=\"CntsRegRow\" style=\"display: none;\" ><td%s><b>%s</td></td><td%s><input type=\"checkbox\" value=\"1\" name=\"%s\" /></td></tr>\n",
+          cl, "Disqualified?", cl, "is_disqualified");
+
+  fprintf(out_f, "<tr id=\"CntsRegRowUseRegPasswd\" class=\"CntsRegRow\" style=\"display: none;\" ><td%s><b>%s:</b></td><td%s><input type=\"checkbox\" name=\"cnts_use_reg_passwd\" onchange=\"updateCntsPasswdVisibility()\" value=\"1\" /></td><td%s>&nbsp;</td></tr>\n",
+          cl, "Use registration password", cl, cl);
+  fprintf(out_f, "<tr id=\"CntsRegRowSetToNull\" class=\"CntsRegRow\" style=\"display: none;\" ><td%s><b>%s:</b></td><td%s><input type=\"checkbox\" name=\"cnts_null_passwd\" onchange=\"updateCntsPasswdVisibility()\" value=\"1\" /></td><td%s>&nbsp;</td></tr>\n",
+          cl, "Set to null", cl, cl);
+  fprintf(out_f, "<tr id=\"CntsRegRowUseRandom\" class=\"CntsRegRow\" style=\"display: none;\" ><td%s><b>%s:</b></td><td%s><input type=\"checkbox\" name=\"cnts_random_passwd\" onchange=\"updateCntsPasswdVisibility()\" value=\"1\" /></td><td%s>&nbsp;</td></tr>\n",
+          cl, "Random contest password", cl, cl);
+  fprintf(out_f, "<tr id=\"CntsRegRowPasswdTemplate\" class=\"CntsRegRow\" style=\"display: none;\" ><td%s><b>%s:</b></td><td%s><input type=\"text\" name=\"cnts_password_template\" size=\"40\" /></td><td%s>&nbsp;</td></tr>\n",
+          cl, "Contest password template", cl, cl);
+  fprintf(out_f, "<tr id=\"CntsRegRowPasswdSha1\" class=\"CntsRegRow\" style=\"display: none;\" ><td%s><b>%s:</b></td><td%s><input type=\"checkbox\" name=\"cnts_sha1\" value=\"1\" /></td><td%s>&nbsp;</td></tr>\n",
+          cl, "Use SHA1", cl, cl);
+
+  fprintf(out_f, "<tr class=\"CntsRegRow\" style=\"display: none;\" ><td%s><b>%s:</b></td><td%s><input type=\"text\" size=\"40\" name=\"cnts_name_template\" /></td><td%s>&nbsp;</td></tr>\n",
+          cl, "User name template", cl, cl);
+
+  fprintf(out_f, "<tr><td%s colspan=\"3\" align=\"center\"><b>%s</b></td></tr>\n",
+          cl, "Group membership");
+
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s><input type=\"checkbox\" onchange=\"changeGroupCreate(this)\" name=\"group_create\" value=\"1\" /></td><td%s>&nbsp;</td></tr>\n",
+          cl, "Add user to a group", cl, cl);
+  hbuf[0] = 0;
+  if (group_id > 0) {
+    snprintf(hbuf, sizeof(hbuf), "%d", group_id);
+  }
+  fprintf(out_f, "<tr class=\"GroupRow\" style=\"display: none;\" ><td%s><b>%s:</b></td><td%s><input type=\"text\" name=\"other_group_id\" size=\"20\" value=\"%s\"/></td><td%s>&nbsp;</td></tr>\n",
+          cl, "Group ID", cl, hbuf, cl);
+
+  fprintf(out_f, "<tr><td%s>&nbsp;</td><td%s><input type=\"submit\" name=\"submit\" value=\"%s\" /></td><td%s>&nbsp;</td></tr>\n",
+          cl, cl, "Create many users", cl);
+  fprintf(out_f, "</table>\n");
+  fprintf(out_f, "</form>\n");
+
+  ss_write_html_footer(out_f);
+
+cleanup:
+  html_armor_free(&ab);
+  return retval;
+}
+
+int
+super_serve_op_USER_CREATE_FROM_CSV_PAGE(
+        FILE *log_f,
+        FILE *out_f,
+        struct super_http_request_info *phr)
+{
+  int retval = 0, row, i;
+  int contest_id = 0, group_id = 0, other_contest_id_2 = 0;
+  const struct contest_desc *cnts = 0;
+  unsigned char buf[1024], hbuf[1024];
+  const unsigned char *cl = 0;
+  const int *cnts_id_list = 0;
+  int cnts_id_count = 0;
+  struct html_armor_buffer ab = HTML_ARMOR_INITIALIZER;
+  const unsigned char *s;
+  opcap_t caps = 0;
+
+  ss_cgi_param_int_opt(phr, "contest_id", &contest_id, 0);
+  ss_cgi_param_int_opt(phr, "group_id", &group_id, 0);
+
+  if (contest_id != 0) {
+    if (contests_get(contest_id, &cnts) < 0 || !cnts) contest_id = 0;
+  }
+  if (group_id < 0) group_id = 0;
+
+  if (get_global_caps(phr, &caps) < 0 || opcaps_check(caps, OPCAP_CREATE_USER) < 0) {
+    FAIL(S_ERR_PERM_DENIED);
+  }
+
+  cnts_id_count = contests_get_list(&cnts_id_list);
+  if (cnts_id_count <= 0 || !cnts_id_list) {
+    cnts_id_count = 0;
+    cnts_id_list = 0;
+  }
+
+  snprintf(buf, sizeof(buf), "serve-control: %s, create users from a CSV file",
+           phr->html_name);
+  ss_write_html_header(out_f, phr, buf, 0, NULL);
+
+  fprintf(out_f, "<script language=\"javascript\">\n");
+  fprintf(out_f,
+          "function changeEmail(form_obj)\n"
+          "{\n"
+          "  if (form_obj.other_email.value != null && form_obj.other_email.value != \"\") {\n"
+          "    document.getElementById(\"SendEmailRow\").style.display = \"\";\n"
+          "    changeSendEmail(form_obj);\n"
+          "  } else {\n"
+          "    document.getElementById(\"SendEmailRow\").style.display = \"none\";\n"
+          "    document.getElementById(\"ConfirmEmailRow\").style.display = \"none\";\n"
+          "  }\n"
+          "}\n");
+  fprintf(out_f,
+          "function changeSendEmail(form_obj)\n"
+          "{\n"
+          "  if (form_obj.send_email.checked) {\n"
+          "    document.getElementById(\"ConfirmEmailRow\").style.display = \"\";\n"
+          "  } else {\n"
+          "    document.getElementById(\"ConfirmEmailRow\").style.display = \"none\";\n"
+          "  }\n"
+          "}\n");
+  fprintf(out_f,
+          "function randomChar()\n"
+          "{\n"
+          "  var str = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\";\n"
+          "  var ind = Math.floor(Math.random() * str.length);\n"
+          "  if (ind < 0 || ind >= str.length) ind = 0;\n"
+          "  return str.charAt(ind);\n"
+          "}\n"
+          "function randomString(length)\n"
+          "{\n"
+          "  var res = \"\";\n"
+          "  for (var i = 0; i < length; ++i) {\n"
+          "    res += randomChar();\n"
+          "  }\n"
+          "  return res;\n"
+          "}\n"
+          "function generateRandomRegPassword()\n"
+          "{\n"
+          "  form_obj = document.getElementById(\"CreateForm\");\n"
+          "  form_obj.reg_random.value = randomString(16);\n"
+          "}\n"
+          "function copyRandomRegPassword()\n"
+          "{\n"
+          "  form_obj = document.getElementById(\"CreateForm\");\n"
+          "  form_obj.reg_password1.value = form_obj.reg_random.value;\n"
+          "  form_obj.reg_password2.value = form_obj.reg_random.value;\n"
+          "}\n"
+          "function generateRandomCntsPassword()\n"
+          "{\n"
+          "  form_obj = document.getElementById(\"CreateForm\");\n"
+          "  form_obj.cnts_random.value = randomString(16);\n"
+          "}\n"
+          "function copyRandomCntsPassword()\n"
+          "{\n"
+          "  form_obj = document.getElementById(\"CreateForm\");\n"
+          "  form_obj.cnts_password1.value = form_obj.cnts_random.value;\n"
+          "  form_obj.cnts_password2.value = form_obj.cnts_random.value;\n"
+          "}\n"
+          "function copyRegPassword()\n"
+          "{\n"
+          "  form_obj = document.getElementById(\"CreateForm\");\n"
+          "  form_obj.cnts_random.value = form_obj.reg_random.value;\n"
+          "  form_obj.cnts_password1.value = form_obj.reg_password1.value;\n"
+          "  form_obj.cnts_password2.value = form_obj.reg_password2.value;\n"
+          "  form_obj.cnts_sha1.checked = form_obj.reg_sha1.checked;\n"
+          "}\n");
+  fprintf(out_f,
+          "function toggleRowsVisibility2(value, tid, rowclass1, rowclass2)\n"
+          "{\n"
+          "  var vis1 = \"\";\n"
+          "  var vis2 = \"\";\n"
+          "  if (value == true) {\n"
+          "    vis1 = \"none\";\n"
+          "  } else {\n"
+          "    vis2 = \"none\";\n"
+          "  }\n"
+          "  var tobj = document.getElementById(tid);\n"
+          "  if (tobj == null) {\n"
+          "    return;\n"
+          "  }\n"
+          "  var trows = tobj.rows;\n"
+          "  if (trows != null) {\n"
+          "    for (var row in trows) {\n"
+          "      if (trows[row].className == rowclass1) {\n"
+          "        trows[row].style.display = vis1;\n"
+          "      } else if (trows[row].className == rowclass2) {\n"
+          "        trows[row].style.display = vis2;\n"
+          "      }\n"
+          "    }\n"
+          "  }\n"
+          "}\n"
+          "function changeCntsRegCreate(obj)\n"
+          "{\n"
+          "  toggleRowsVisibility2(obj.checked, \"CreateUserTable\", \"CntsRegRow0\", \"CntsRegRow\");\n"
+          "  updateCntsPasswdVisibility();\n"
+          "}\n"
+          "function changeGroupCreate(obj)\n"
+          "{\n"
+          "  toggleRowsVisibility2(obj.checked, \"CreateUserTable\", \"GroupRow0\", \"GroupRow\");\n"
+          "}\n"
+          "");
+  fprintf(out_f,
+          "function updateCnts1()\n"
+          "{\n"
+          "  var obj1 = document.getElementById(\"cnts1\");\n"
+          "  var obj2 = document.getElementById(\"cnts2\");\n"
+          "  var value = obj1.value;\n"
+          "  var i;\n"
+          "  for (i = 0; i < obj2.options.length; ++i) {\n"
+          "    if (obj2.options[i].value == value) {\n"
+          "      obj2.options.selectedIndex = i;\n"
+          "      break;\n"
+          "    }\n"
+          "  }\n"
+          "  updateCntsPasswdVisibility();\n"
+          "}\n");
+  fprintf(out_f,
+          "function updateCnts2()\n"
+          "{\n"
+          "  var obj1 = document.getElementById(\"cnts1\");\n"
+          "  var obj2 = document.getElementById(\"cnts2\");\n"
+          "  var value = obj2.options[obj2.selectedIndex].value;\n"
+          "  obj1.value = value;\n"
+          "  updateCntsPasswdVisibility();\n"
+          "}\n");
+  fprintf(out_f, "var cnts_passwd_enabled = { ");
+  row = 0;
+  for (i = 0; i < cnts_id_count; ++i) {
+    other_contest_id_2 = cnts_id_list[i];
+    if (other_contest_id_2 <= 0) continue;
+    if (contests_get(other_contest_id_2, &cnts) < 0 || !cnts) continue;
+    if (!cnts->disable_team_password) {
+      if (row) fprintf(out_f, ", ");
+      ++row;
+      fprintf(out_f, "%d : true", other_contest_id_2);
+    }
+  }
+  fprintf(out_f, "};\n");
+  // CntsRegRowUseRegPasswd   cnts_use_reg_passwd
+  // CntsRegRowSetToNull      cnts_null_passwd
+  // CntsRegRowUseRandom      cnts_random_passwd
+  // CntsRegRowPasswdSha1     cnts_sha1
+  fprintf(out_f,
+          "function updateCntsPasswdVisibility()\n"
+          "{\n"
+          "  form_obj = document.getElementById(\"CreateForm\");\n"
+          "  if (!form_obj.reg_cnts_create.checked || !cnts_passwd_enabled[form_obj.other_contest_id_1.value]) {\n"
+          "    form_obj.cnts_use_reg_passwd.checked = false;\n"
+          "    form_obj.cnts_null_passwd.checked = false;\n"
+          "    form_obj.cnts_random_passwd.checked = false;\n"
+          "    form_obj.cnts_sha1.checked = false;\n"
+          "    document.getElementById(\"CntsRegRowUseRegPasswd\").style.display = \"none\";\n"
+          "    document.getElementById(\"CntsRegRowSetToNull\").style.display = \"none\";\n"
+          "    document.getElementById(\"CntsRegRowUseRandom\").style.display = \"none\";\n"
+          "    document.getElementById(\"CntsRegRowPasswdSha1\").style.display = \"none\";\n"
+          "  } else {\n"
+          "    document.getElementById(\"CntsRegRowUseRegPasswd\").style.display = \"\";\n"
+          "    if (form_obj.cnts_use_reg_passwd.checked) {\n"
+          "      form_obj.cnts_null_passwd.checked = false;\n"
+          "      form_obj.cnts_random_passwd.checked = false;\n"
+          "      form_obj.cnts_sha1.checked = false;\n"
+          "      document.getElementById(\"CntsRegRowSetToNull\").style.display = \"none\";\n"
+          "      document.getElementById(\"CntsRegRowUseRandom\").style.display = \"none\";\n"
+          "      document.getElementById(\"CntsRegRowPasswdSha1\").style.display = \"none\";\n"
+          "    } else {\n"
+          "      document.getElementById(\"CntsRegRowSetToNull\").style.display = \"\";\n"
+          "      if (form_obj.cnts_null_passwd.checked) {\n"
+          "        form_obj.cnts_random_passwd.checked = false;\n"
+          "        form_obj.cnts_sha1.checked = false;\n"
+          "        document.getElementById(\"CntsRegRowUseRandom\").style.display = \"none\";\n"
+          "        document.getElementById(\"CntsRegRowPasswdSha1\").style.display = \"none\";\n"
+          "      } else {\n"
+          "        document.getElementById(\"CntsRegRowUseRandom\").style.display = \"\";\n"
+          "        if (form_obj.cnts_random_passwd.checked) {\n"
+          "          form_obj.cnts_password_template.value = \"\";\n"
+          "          form_obj.cnts_sha1.checked = false;\n"
+          "          document.getElementById(\"CntsRegRowPasswdSha1\").style.display = \"none\";\n"
+          "        } else {\n"
+          "          document.getElementById(\"CntsRegRowPasswdSha1\").style.display = \"\";\n"
+          "        }\n"
+          "      }\n"
+          "    }\n"
+          "  }\n"
+          "}\n");
+  fprintf(out_f, "</script>\n");
+
+  fprintf(out_f, "<h1>%s</h1>\n<br/>\n", buf);
+
+  print_top_navigation_links(log_f, out_f, phr, contest_id, group_id, 0, NULL);
+
+  html_start_form_id(out_f, 2, phr->self_url, "CreateForm", "");
+  html_hidden(out_f, "SID", "%016llx", phr->session_id);
+  html_hidden(out_f, "action", "%d", SSERV_CMD_HTTP_REQUEST);
+  html_hidden(out_f, "op", "%d", SSERV_OP_USER_CREATE_ONE_ACTION);
+  if (contest_id > 0) {
+    html_hidden(out_f, "contest_id", "%d", contest_id);
+  }
+  if (group_id > 0) {
+    html_hidden(out_f, "group_id", "%d", group_id);
+  }
+  cl = " class=\"b0\"";
+  fprintf(out_f, "<table%s id=\"CreateUserTable\">\n", cl);
+  fprintf(out_f, "<tr id=\"SendEmailRow\"><td%s><b>%s:</b></td><td%s><input type=\"checkbox\" onchange=\"changeSendEmail(this.form)\" name=\"send_email\" value=\"1\" /></td><td%s>&nbsp;</td></tr>\n",
+          cl, "Send registration e-mail", cl, cl);
+  fprintf(out_f, "<tr id=\"ConfirmEmailRow\" style=\"display: none;\" ><td%s><b>%s:</b></td><td%s><input type=\"checkbox\" name=\"confirm_email\" value=\"1\" /></td><td%s>&nbsp;</td></tr>\n",
+          cl, "Confirm e-mail by user", cl, cl);
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s><input type=\"checkbox\" name=\"reg_random\" value=\"1\" /></td><td%s>&nbsp;</td></tr>\n",
+          cl, "Use random password", cl, cl);
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s><input type=\"checkbox\" name=\"reg_sha1\" value=\"1\" /></td><td%s>&nbsp;</td></tr>\n",
+          cl, "Use SHA1", cl, cl);
+
+  for (row = 0; user_flag_rows[row].field_id > 0; ++row) {
+    fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s><input type=\"checkbox\" name=\"field_%d\" value=\"1\" /></td><td%s>&nbsp;</td></tr>\n",
+            cl, user_flag_rows[row].field_desc, cl, user_flag_rows[row].field_id, cl);
+  }
+
+  fprintf(out_f, "<tr><td%s colspan=\"3\" align=\"center\"><b>%s</b></td></tr>\n",
+          cl, "Contest registration");
+
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s><input type=\"checkbox\" onchange=\"changeCntsRegCreate(this)\" name=\"reg_cnts_create\" value=\"1\" /></td><td%s>&nbsp;</td></tr>\n",
+          cl, "Create a contest registration", cl, cl);
+
+  hbuf[0] = 0;
+  if (contest_id > 0) {
+    snprintf(hbuf, sizeof(hbuf), "%d", contest_id);
+  }
+  fprintf(out_f, "<tr class=\"CntsRegRow\" style=\"display: none;\" ><td%s><b>%s:</b></td><td%s><input id=\"cnts1\" onchange=\"updateCnts1()\" type=\"text\" name=\"other_contest_id_1\" size=\"20\" value=\"%s\"/></td><td%s>&nbsp;</td></tr>\n",
+          cl, "Contest ID", cl, hbuf, cl);
+  if (cnts_id_count > 0) {
+    fprintf(out_f, "<tr class=\"CntsRegRow\" style=\"display: none;\" ><td%s><b>%s:</b></td><td%s>", cl, "Contest name", cl);
+    fprintf(out_f, "<select id=\"cnts2\" onchange=\"updateCnts2()\" name=\"other_contest_id_2\"><option value=\"0\"></option>");
+    for (i = 0; i < cnts_id_count; ++i) {
+      other_contest_id_2 = cnts_id_list[i];
+      if (other_contest_id_2 <= 0) continue;
+      if (contests_get(other_contest_id_2, &cnts) < 0 || !cnts) continue;
+      if (cnts->closed) continue;
+      s = "";
+      if (contest_id > 0 && cnts->id == contest_id) {
+        s = " selected=\"selected\"";
+      }
+      fprintf(out_f, "<option value=\"%d\"%s>%s</option>", other_contest_id_2, s, ARMOR(cnts->name));
+    }
+    fprintf(out_f, "</select>");
+    fprintf(out_f, "</td><td%s>&nbsp;</td></tr>\n", cl);
+  }
+  fprintf(out_f, "<tr class=\"CntsRegRow\" style=\"display: none;\" ><td%s><b>%s:</b></td><td%s>", cl, "Status", cl);
+  ss_select(out_f, "cnts_status", (const unsigned char* []) { "OK", "Pending", "Rejected", NULL }, 1);
+  fprintf(out_f, "</td></tr>\n");
+  fprintf(out_f, "<tr class=\"CntsRegRow\" style=\"display: none;\" ><td%s><b>%s</td></td><td%s><input type=\"checkbox\" value=\"1\" name=\"%s\" /></td></tr>\n",
+          cl, "Invisible?", cl, "is_invisible");
+  fprintf(out_f, "<tr class=\"CntsRegRow\" style=\"display: none;\" ><td%s><b>%s</td></td><td%s><input type=\"checkbox\" value=\"1\" name=\"%s\" /></td></tr>\n",
+          cl, "Banned?", cl, "is_banned");
+  fprintf(out_f, "<tr class=\"CntsRegRow\" style=\"display: none;\" ><td%s><b>%s</td></td><td%s><input type=\"checkbox\" value=\"1\" name=\"%s\" /></td></tr>\n",
+          cl, "Locked?", cl, "is_locked");
+  fprintf(out_f, "<tr class=\"CntsRegRow\" style=\"display: none;\" ><td%s><b>%s</td></td><td%s><input type=\"checkbox\" value=\"1\" name=\"%s\" /></td></tr>\n",
+          cl, "Incomplete?", cl, "is_incomplete");
+  fprintf(out_f, "<tr class=\"CntsRegRow\" style=\"display: none;\" ><td%s><b>%s</td></td><td%s><input type=\"checkbox\" value=\"1\" name=\"%s\" /></td></tr>\n",
+          cl, "Disqualified?", cl, "is_disqualified");
+
+  fprintf(out_f, "<tr id=\"CntsRegRowUseRegPasswd\" class=\"CntsRegRow\" style=\"display: none;\" ><td%s><b>%s:</b></td><td%s><input type=\"checkbox\" name=\"cnts_use_reg_passwd\" onchange=\"updateCntsPasswdVisibility()\" value=\"1\" /></td><td%s>&nbsp;</td></tr>\n",
+          cl, "Use registration password", cl, cl);
+  fprintf(out_f, "<tr id=\"CntsRegRowSetToNull\" class=\"CntsRegRow\" style=\"display: none;\" ><td%s><b>%s:</b></td><td%s><input type=\"checkbox\" name=\"cnts_null_passwd\" onchange=\"updateCntsPasswdVisibility()\" value=\"1\" /></td><td%s>&nbsp;</td></tr>\n",
+          cl, "Set to null", cl, cl);
+  fprintf(out_f, "<tr id=\"CntsRegRowUseRandom\" class=\"CntsRegRow\" style=\"display: none;\" ><td%s><b>%s:</b></td><td%s><input type=\"checkbox\" name=\"cnts_random_passwd\" onchange=\"updateCntsPasswdVisibility()\" value=\"1\" /></td><td%s>&nbsp;</td></tr>\n",
+          cl, "Random contest password", cl, cl);
+  fprintf(out_f, "<tr id=\"CntsRegRowPasswdSha1\" class=\"CntsRegRow\" style=\"display: none;\" ><td%s><b>%s:</b></td><td%s><input type=\"checkbox\" name=\"cnts_sha1\" value=\"1\" /></td><td%s>&nbsp;</td></tr>\n",
+          cl, "Use SHA1", cl, cl);
+
+  fprintf(out_f, "<tr><td%s colspan=\"3\" align=\"center\"><b>%s</b></td></tr>\n",
+          cl, "Group membership");
+
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s><input type=\"checkbox\" onchange=\"changeGroupCreate(this)\" name=\"group_create\" value=\"1\" /></td><td%s>&nbsp;</td></tr>\n",
+          cl, "Add user to a group", cl, cl);
+  hbuf[0] = 0;
+  if (group_id > 0) {
+    snprintf(hbuf, sizeof(hbuf), "%d", group_id);
+  }
+  fprintf(out_f, "<tr class=\"GroupRow\" style=\"display: none;\" ><td%s><b>%s:</b></td><td%s><input type=\"text\" name=\"other_group_id\" size=\"20\" value=\"%s\"/></td><td%s>&nbsp;</td></tr>\n",
+          cl, "Group ID", cl, hbuf, cl);
+
+  fprintf(out_f, "<tr><td%s colspan=\"3\" align=\"center\"><b>%s</b></td></tr>\n",
+          cl, "File");
+
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s><input type=\"text\" name=\"separator\" size=\"20\" value=\";\"/></td><td%s>&nbsp;</td></tr>\n",
+          cl, "Field separator", cl, cl);
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>", cl, "Charset", cl);
+  charset_html_select(out_f, NULL, NULL);
+  fprintf(out_f, "</td><td%s>&nbsp;</td></tr>\n", cl);
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s><input type=\"file\" name=\"csv_file\" /></td><td%s>&nbsp;</td></tr>\n",
+          cl, "CSV File", cl, cl);
+
+  fprintf(out_f, "<tr><td%s>&nbsp;</td><td%s><input type=\"submit\" name=\"submit\" value=\"%s\" /></td><td%s>&nbsp;</td></tr>\n",
+          cl, cl, "Create users", cl);
+  fprintf(out_f, "</table>\n");
+  fprintf(out_f, "</form>\n");
+
+  ss_write_html_footer(out_f);
+
+cleanup:
+  html_armor_free(&ab);
+  return retval;
+}
+
+int
+super_serve_op_USER_CHANGE_PASSWORD_ACTION(
+        FILE *log_f,
+        FILE *out_f,
+        struct super_http_request_info *phr)
+{
+  int retval = 0, r;
+  int contest_id = 0, group_id = 0, other_user_id = 0, next_op = 0, usesha1 = 0;
+  const struct contest_desc *cnts = 0;
+  unsigned char *xml_text = 0;
+  struct userlist_user *u = 0;
+  opcap_t caps = 0;
+  const unsigned char *s = 0;
+  unsigned char *reg_password1 = 0;
+  unsigned char *reg_password2 = 0;
+
+  ss_cgi_param_int_opt(phr, "contest_id", &contest_id, 0);
+  ss_cgi_param_int_opt(phr, "group_id", &group_id, 0);
+  ss_cgi_param_int_opt(phr, "next_op", &next_op, 0);
+  if (contest_id != 0) {
+    if (contests_get(contest_id, &cnts) < 0 || !cnts) contest_id = 0;
+  }
+
+  s = 0;
+  if (ss_cgi_param(phr, "reg_password1", &s) <= 0 || !s) FAIL(S_ERR_UNSPEC_PASSWD1);
+  reg_password1 = fix_string(s);
+  if (!reg_password1 || !*reg_password1) FAIL(S_ERR_UNSPEC_PASSWD1);
+  if (strlen(reg_password1) > 1024) FAIL(S_ERR_INV_PASSWD1);
+  s = 0;
+  if (ss_cgi_param(phr, "reg_password2", &s) <= 0 || !s) FAIL(S_ERR_UNSPEC_PASSWD2);
+  reg_password2 = fix_string(s);
+  if (!reg_password2 || !*reg_password2) FAIL(S_ERR_UNSPEC_PASSWD2);
+  if (strlen(reg_password2) > 1024) FAIL(S_ERR_INV_PASSWD2);
+  if (strcmp(reg_password1, reg_password2) != 0) FAIL(S_ERR_PASSWDS_DIFFER);
+
+  ss_cgi_param_int_opt(phr, "usesha1", &usesha1, 0);
+  if (usesha1 != 1) usesha1 = 0;
+
+  if (phr->priv_level <= 0) FAIL(S_ERR_PERM_DENIED);
+  if (get_global_caps(phr, &caps) < 0) FAIL(S_ERR_PERM_DENIED);
+  if (opcaps_check(caps, OPCAP_EDIT_PASSWD) < 0 && opcaps_check(caps, OPCAP_PRIV_EDIT_PASSWD) < 0)
+    FAIL(S_ERR_PERM_DENIED);
+
+  if (ss_cgi_param_int(phr, "other_user_id", &other_user_id) < 0) FAIL(S_ERR_INV_USER_ID);
+  if (!phr->userlist_clnt) FAIL(S_ERR_NO_CONNECTION);
+  r = userlist_clnt_get_info(phr->userlist_clnt, ULS_PRIV_GET_USER_INFO,
+                             other_user_id, 0, &xml_text);
+  if (r < 0) {
+    if (r == -ULS_ERR_BAD_UID) FAIL(S_ERR_INV_USER_ID);
+    FAIL(S_ERR_DB_ERROR);
+  }
+  if (!(u = userlist_parse_user_str(xml_text))) FAIL(S_ERR_DB_ERROR);
+  if (is_globally_privileged(phr, u) && opcaps_check(caps, OPCAP_PRIV_EDIT_PASSWD) < 0)
+    FAIL(S_ERR_PERM_DENIED);
+  else if (opcaps_check(caps, OPCAP_EDIT_PASSWD) < 0)
+    FAIL(S_ERR_PERM_DENIED);
+
+  r = ULS_PRIV_SET_REG_PASSWD_PLAIN;
+  if (usesha1) r = ULS_PRIV_SET_REG_PASSWD_SHA1;
+
+  r = userlist_clnt_set_passwd(phr->userlist_clnt, r, other_user_id, 0, "", reg_password1);
+  if (r < 0) FAIL(S_ERR_DB_ERROR);
+
+  if (next_op == SSERV_OP_USER_DETAIL_PAGE) {
+    ss_redirect_2(out_f, phr, SSERV_OP_USER_DETAIL_PAGE, contest_id, group_id, other_user_id, NULL);
+  } else {
+    ss_redirect_2(out_f, phr, SSERV_OP_USER_BROWSE_PAGE, contest_id, group_id, 0, NULL);
+  }
+
+cleanup:
+  userlist_free(&u->b); u = 0;
+  xfree(xml_text); xml_text = 0;
+  xfree(reg_password1); reg_password1 = 0;
+  xfree(reg_password2); reg_password2 = 0;
+  return retval;
+}
+
+int
+super_serve_op_USER_CHANGE_CNTS_PASSWORD_ACTION(
+        FILE *log_f,
+        FILE *out_f,
+        struct super_http_request_info *phr)
+{
+  int retval = 0, r;
+  int contest_id = 0, group_id = 0, other_user_id = 0;
+  int next_op = 0;
+  const struct contest_desc *cnts = 0;
+  int useregpasswd = 0, settonull = 0, usesha1 = 0;
+  unsigned char *cnts_password1 = 0;
+  unsigned char *cnts_password2 = 0;
+  const unsigned char *s = 0;
+  opcap_t gcaps = 0, ccaps = 0, fcaps = 0;
+  struct userlist_user *u = 0;
+  unsigned char *xml_text = 0;
+
+  if (ss_cgi_param_int(phr, "contest_id", &contest_id) <= 0) FAIL(S_ERR_INV_CONTEST);
+  if (contest_id <= 0 || contests_get(contest_id, &cnts) < 0 || !cnts)
+    FAIL(S_ERR_INV_CONTEST);
+
+  ss_cgi_param_int_opt(phr, "group_id", &group_id, 0);
+  ss_cgi_param_int_opt(phr, "next_op", &next_op, 0);
+
+  ss_cgi_param_int_opt(phr, "useregpasswd", &useregpasswd, 0);
+  if (useregpasswd != 1) useregpasswd = 0;
+  ss_cgi_param_int_opt(phr, "settonull", &settonull, 0);
+  if (settonull != 1) settonull = 0;
+  ss_cgi_param_int_opt(phr, "usesha1", &usesha1, 0);
+  if (usesha1 != 1) usesha1 = 0;
+
+  if (!useregpasswd && !settonull) {
+    s = 0;
+    if (ss_cgi_param(phr, "cnts_password1", &s) <= 0 || !s) FAIL(S_ERR_UNSPEC_PASSWD1);
+    cnts_password1 = fix_string(s);
+    if (!cnts_password1 || !*cnts_password1) FAIL(S_ERR_UNSPEC_PASSWD1);
+    if (strlen(cnts_password1) > 1024) FAIL(S_ERR_INV_PASSWD1);
+    s = 0;
+    if (ss_cgi_param(phr, "cnts_password2", &s) <= 0 || !s) FAIL(S_ERR_UNSPEC_PASSWD2);
+    cnts_password2 = fix_string(s);
+    if (!cnts_password2 || !*cnts_password2) FAIL(S_ERR_UNSPEC_PASSWD2);
+    if (strlen(cnts_password2) > 1024) FAIL(S_ERR_INV_PASSWD2);
+    if (strcmp(cnts_password1, cnts_password2) != 0) FAIL(S_ERR_PASSWDS_DIFFER);
+  }
+
+  if (phr->priv_level <= 0) FAIL(S_ERR_PERM_DENIED);
+  get_global_caps(phr, &gcaps);
+  get_contest_caps(phr, cnts, &ccaps);
+  fcaps = gcaps | ccaps;
+  if (opcaps_check(fcaps, OPCAP_EDIT_PASSWD) < 0 && opcaps_check(fcaps, OPCAP_PRIV_EDIT_PASSWD) < 0)
+    FAIL(S_ERR_PERM_DENIED);
+
+  if (ss_cgi_param_int(phr, "other_user_id", &other_user_id) < 0) FAIL(S_ERR_INV_USER_ID);
+  if (!phr->userlist_clnt) FAIL(S_ERR_NO_CONNECTION);
+  r = userlist_clnt_get_info(phr->userlist_clnt, ULS_PRIV_GET_USER_INFO,
+                             other_user_id, contest_id, &xml_text);
+  if (r < 0) {
+    if (r == -ULS_ERR_BAD_UID) FAIL(S_ERR_INV_USER_ID);
+    FAIL(S_ERR_DB_ERROR);
+  }
+  if (!(u = userlist_parse_user_str(xml_text))) FAIL(S_ERR_DB_ERROR);
+
+  if (is_globally_privileged(phr, u)) {
+    if (opcaps_check(gcaps, OPCAP_PRIV_EDIT_PASSWD) < 0) FAIL(S_ERR_PERM_DENIED);
+  } else if (is_contest_privileged(cnts, u)) {
+    if (opcaps_check(fcaps, OPCAP_PRIV_EDIT_PASSWD) < 0) FAIL(S_ERR_PERM_DENIED);
+  } else {
+    if (opcaps_check(fcaps, OPCAP_EDIT_PASSWD) < 0) FAIL(S_ERR_PERM_DENIED);
+  }
+
+  if (settonull) {
+    r = userlist_clnt_delete_field(phr->userlist_clnt, ULS_DELETE_FIELD,
+                                   other_user_id, contest_id, 0,
+                                   USERLIST_NC_TEAM_PASSWD);
+  } else if (useregpasswd) {
+    r = userlist_clnt_register_contest(phr->userlist_clnt, ULS_COPY_TO_TEAM, other_user_id,
+                                       contest_id, 0, 0);
+  } else {
+    r = ULS_PRIV_SET_CNTS_PASSWD_PLAIN;
+    if (usesha1) r = ULS_PRIV_SET_CNTS_PASSWD_SHA1;
+
+    r = userlist_clnt_set_passwd(phr->userlist_clnt, r, other_user_id, 0, "", cnts_password1);
+  }
+  if (r < 0) FAIL(S_ERR_DB_ERROR);
+
+  if (next_op == SSERV_OP_USER_DETAIL_PAGE) {
+    ss_redirect_2(out_f, phr, SSERV_OP_USER_DETAIL_PAGE, contest_id, group_id, other_user_id, NULL);
+  } else {
+    ss_redirect_2(out_f, phr, SSERV_OP_USER_BROWSE_PAGE, contest_id, group_id, 0, NULL);
+  }
+
+cleanup:
+  userlist_free(&u->b); u = 0;
+  xfree(xml_text); xml_text = 0;
+  xfree(cnts_password1); cnts_password1 = 0;
+  xfree(cnts_password2); cnts_password2 = 0;
+  return retval;
+}
+
+int
+super_serve_op_USER_CREATE_ONE_ACTION(
+        FILE *log_f,
+        FILE *out_f,
+        struct super_http_request_info *phr)
+{
+  int retval = 0, r;
+  opcap_t caps = 0;
+  const struct contest_desc *cnts = 0;
+  int other_user_id = 0;
+  unsigned char *xml_text = 0;
+
+  struct ss_op_param_USER_CREATE_ONE_ACTION params;
+  memset(&params, 0, sizeof(params));
+  retval = ss_parse_params(phr, &meta_ss_op_param_USER_CREATE_ONE_ACTION_methods, &params);
+  if (retval < 0) goto cleanup;
+
+  if (params.contest_id > 0) {
+    cnts = 0;
+    if (contests_get(params.contest_id, &cnts) < 0 || !cnts) {
+      params.contest_id = 0;
+    }
+  }
+  cnts = 0;
+  if (params.reg_cnts_create) {
+    if (contests_get(params.other_contest_id_1, &cnts) < 0 || !cnts) {
+      FAIL(S_ERR_INV_CONTEST);
+    }
+  } else {
+    params.other_contest_id_1 = 0;
+  }
+
+  if (params.group_create) {
+    r = userlist_clnt_list_all_users(phr->userlist_clnt, ULS_LIST_GROUP_USERS,
+                                     params.other_group_id, &xml_text);
+    if (r < 0) FAIL(S_ERR_INV_GROUP_ID);
+  } else {
+    params.other_group_id = 0;
+  }
+
+  if (get_global_caps(phr, &caps) < 0 || opcaps_check(caps, OPCAP_CREATE_USER) < 0) {
+    FAIL(S_ERR_PERM_DENIED);
+  }
+  if (cnts) {
+    if (get_contest_caps(phr, cnts, &caps) < 0 || opcaps_check(caps, OPCAP_CREATE_REG) < 0) {
+      FAIL(S_ERR_PERM_DENIED);
+    }
+  }
+
+  if (!params.other_login || !*params.other_login) FAIL(S_ERR_UNSPEC_LOGIN);
+  if (!params.reg_password1 || !*params.reg_password1) FAIL(S_ERR_UNSPEC_PASSWD1);
+  if (!params.reg_password2 || !*params.reg_password2) FAIL(S_ERR_UNSPEC_PASSWD2);
+  if (strcmp(params.reg_password1, params.reg_password2) != 0) FAIL(S_ERR_PASSWDS_DIFFER);
+  if (params.cnts_status < 0 || params.cnts_status >= USERLIST_REG_LAST) params.cnts_status = USERLIST_REG_PENDING;
+  if (cnts && !cnts->disable_team_password && !params.cnts_use_reg_passwd && !params.cnts_null_passwd) {
+    if (!params.cnts_password1 || !*params.cnts_password1) FAIL(S_ERR_UNSPEC_PASSWD1);
+    if (!params.cnts_password2 || !*params.cnts_password2) FAIL(S_ERR_UNSPEC_PASSWD2);
+    if (strcmp(params.cnts_password1, params.cnts_password2) != 0) FAIL(S_ERR_PASSWDS_DIFFER);
+  }
+  if (params.other_email && *params.other_email && !is_valid_email_address(params.other_email))
+    FAIL(S_ERR_INV_EMAIL);
+
+  struct userlist_pk_create_user_2 up;
+  memset(&up, 0, sizeof(up));
+  up.send_email_flag = params.send_email;
+  up.confirm_email_flag = params.confirm_email;
+  up.use_sha1_flag = params.reg_sha1;
+  up.is_privileged_flag = params.field_1;
+  up.is_invisible_flag = params.field_2;
+  up.is_banned_flag = params.field_3;
+  up.is_locked_flag = params.field_4;
+  up.show_login_flag = params.field_5;
+  up.show_email_flag = params.field_6;
+  up.read_only_flag = params.field_7;
+  up.never_clean_flag = params.field_8;
+  up.simple_registration_flag = params.field_9;
+  up.contest_id = params.other_contest_id_1;
+  up.cnts_status = params.cnts_status;
+  up.cnts_is_invisible_flag = params.is_invisible;
+  up.cnts_is_banned_flag = params.is_banned;
+  up.cnts_is_locked_flag = params.is_locked;
+  up.cnts_is_incomplete_flag = params.is_incomplete;
+  up.cnts_is_disqualified_flag = params.is_disqualified;
+  up.cnts_use_reg_passwd_flag = params.cnts_use_reg_passwd;
+  up.cnts_set_null_passwd_flag = params.cnts_null_passwd;
+  up.cnts_use_sha1_flag = params.cnts_sha1;
+  up.group_id = params.other_group_id;
+
+  r = userlist_clnt_create_user_2(phr->userlist_clnt, ULS_CREATE_USER_2, &up,
+                                  params.other_login, params.other_email,
+                                  params.reg_password1, params.cnts_password1,
+                                  params.cnts_name, &other_user_id);
+  if (r < 0 && r == -ULS_ERR_LOGIN_USED) {
+    FAIL(S_ERR_DUPLICATED_LOGIN);
+  }
+  if (r < 0 || other_user_id <= 0) {
+    FAIL(S_ERR_DB_ERROR);
+  }
+
+  ss_redirect_2(out_f, phr, SSERV_OP_USER_BROWSE_PAGE, params.contest_id, params.group_id, 0, NULL);
+
+cleanup:
+  xfree(xml_text); xml_text = 0;
+  meta_destroy_fields(&meta_ss_op_param_USER_CREATE_ONE_ACTION_methods, &params);
+  return retval;
+}
+
+static unsigned char **create_many_sorted_logins;
+static int create_many_sort_func(const void *p1, const void *p2)
+{
+  return strcmp(*(const unsigned char**) p1, *(const unsigned char **) p2);
+}
+
+int
+super_serve_op_USER_CREATE_MANY_ACTION(
+        FILE *log_f,
+        FILE *out_f,
+        struct super_http_request_info *phr)
+{
+  int retval = 0, r, i;
+  opcap_t caps = 0;
+  const struct contest_desc *cnts = 0;
+  unsigned char *xml_text = 0;
+  int serial_count = 0, cur_serial;
+  unsigned char **login_strs = 0;
+  unsigned char buf[1024];
+  unsigned char **reg_password_strs = 0;
+  unsigned char **cnts_password_strs = 0;
+  unsigned char **cnts_name_strs = 0;
+  int other_user_id = 0;
+
+  struct ss_op_param_USER_CREATE_MANY_ACTION params;
+  memset(&params, 0, sizeof(params));
+  retval = ss_parse_params(phr, &meta_ss_op_param_USER_CREATE_MANY_ACTION_methods, &params);
+  if (retval < 0) goto cleanup;
+
+  if (params.contest_id > 0) {
+    cnts = 0;
+    if (contests_get(params.contest_id, &cnts) < 0 || !cnts) {
+      params.contest_id = 0;
+    }
+  }
+  cnts = 0;
+  if (params.reg_cnts_create) {
+    if (contests_get(params.other_contest_id_1, &cnts) < 0 || !cnts) {
+      FAIL(S_ERR_INV_CONTEST);
+    }
+  } else {
+    params.other_contest_id_1 = 0;
+  }
+
+  if (params.group_create) {
+    r = userlist_clnt_list_all_users(phr->userlist_clnt, ULS_LIST_GROUP_USERS,
+                                     params.other_group_id, &xml_text);
+    if (r < 0) FAIL(S_ERR_INV_GROUP_ID);
+  } else {
+    params.other_group_id = 0;
+  }
+
+  if (get_global_caps(phr, &caps) < 0 || opcaps_check(caps, OPCAP_CREATE_USER) < 0) {
+    FAIL(S_ERR_PERM_DENIED);
+  }
+  if (cnts) {
+    if (get_contest_caps(phr, cnts, &caps) < 0 || opcaps_check(caps, OPCAP_CREATE_REG) < 0) {
+      FAIL(S_ERR_PERM_DENIED);
+    }
+  }
+
+  if (params.first_serial < 0 || params.first_serial >= 1000000000) FAIL(S_ERR_INV_FIRST_SERIAL);
+  if (params.last_serial < 0 || params.last_serial >= 1000000000) FAIL(S_ERR_INV_LAST_SERIAL);
+  if (params.first_serial > params.last_serial) FAIL(S_ERR_INV_RANGE);
+  serial_count = params.last_serial - params.first_serial + 1;
+  if (serial_count > 1000) FAIL(S_ERR_INV_RANGE);
+  if (!params.login_template || !*params.login_template) FAIL(S_ERR_INV_LOGIN_TEMPLATE);
+
+  int printf_arg_types[10];
+  memset(printf_arg_types, 0, sizeof(printf_arg_types));
+  int printf_arg_count = parse_printf_format(params.login_template, 10, printf_arg_types);
+  if (printf_arg_count != 1) FAIL(S_ERR_INV_LOGIN_TEMPLATE);
+  if ((printf_arg_types[0] & ~PA_FLAG_MASK) != PA_INT) FAIL(S_ERR_INV_LOGIN_TEMPLATE);
+
+  if (create_many_sorted_logins) {
+    xfree(create_many_sorted_logins);
+    create_many_sorted_logins = 0;
+  }
+  XCALLOC(create_many_sorted_logins, serial_count);
+  XCALLOC(login_strs, serial_count);
+  for (i = 0, cur_serial = params.first_serial; cur_serial <= params.last_serial; ++i, ++cur_serial) {
+    snprintf(buf, sizeof(buf), params.login_template, cur_serial);
+    if (strlen(buf) > 1000) FAIL(S_ERR_INV_LOGIN_TEMPLATE);
+    login_strs[i] = xstrdup(buf);
+    create_many_sorted_logins[i] = login_strs[i];
+  }
+  /* check login uniqueness */
+  qsort(create_many_sorted_logins, serial_count, sizeof(create_many_sorted_logins), create_many_sort_func);
+  for (i = 1; i < serial_count; ++i) {
+    if (!strcmp(create_many_sorted_logins[i - 1], create_many_sorted_logins[i]))
+      FAIL(S_ERR_INV_LOGIN_TEMPLATE);
+  }
+
+  XCALLOC(reg_password_strs, serial_count);
+  if (!params.reg_random) {
+    if (!params.reg_password_template || !*params.reg_password_template) FAIL(S_ERR_INV_REG_PASSWORD_TEMPLATE);
+    memset(printf_arg_types, 0, sizeof(printf_arg_types));
+    printf_arg_count = parse_printf_format(params.reg_password_template, 10, printf_arg_types);
+    if (printf_arg_count != 0 && printf_arg_count != 1)
+      FAIL(S_ERR_INV_REG_PASSWORD_TEMPLATE);
+    if (printf_arg_count == 1 && (printf_arg_types[0] & ~PA_FLAG_MASK) != PA_INT)
+      FAIL(S_ERR_INV_REG_PASSWORD_TEMPLATE);
+    for (i = 0, cur_serial = params.first_serial; cur_serial <= params.last_serial; ++i, ++cur_serial) {
+      snprintf(buf, sizeof(buf), params.reg_password_template, cur_serial);
+      if (strlen(buf) > 1000) FAIL(S_ERR_INV_REG_PASSWORD_TEMPLATE);
+      reg_password_strs[i] = xstrdup(buf);
+    }
+  }
+
+  XCALLOC(cnts_password_strs, serial_count);
+  if (cnts && !cnts->disable_team_password && !params.cnts_use_reg_passwd
+      && !params.cnts_null_passwd && !params.cnts_random_passwd) {
+    if (!params.cnts_password_template || !*params.cnts_password_template) FAIL(S_ERR_INV_CNTS_PASSWORD_TEMPLATE);
+    memset(printf_arg_types, 0, sizeof(printf_arg_types));
+    printf_arg_count = parse_printf_format(params.cnts_password_template, 10, printf_arg_types);
+    if (printf_arg_count != 0 && printf_arg_count != 1)
+      FAIL(S_ERR_INV_CNTS_PASSWORD_TEMPLATE);
+    if (printf_arg_count == 1 && (printf_arg_types[0] & ~PA_FLAG_MASK) != PA_INT)
+      FAIL(S_ERR_INV_CNTS_PASSWORD_TEMPLATE);
+    for (i = 0, cur_serial = params.first_serial; cur_serial <= params.last_serial; ++i, ++cur_serial) {
+      snprintf(buf, sizeof(buf), params.cnts_password_template, cur_serial);
+      if (strlen(buf) > 1000) FAIL(S_ERR_INV_CNTS_PASSWORD_TEMPLATE);
+      cnts_password_strs[i] = xstrdup(buf);
+    }
+  }
+
+  XCALLOC(cnts_name_strs, serial_count);
+  if (cnts) {
+    if (!params.cnts_name_template || !*params.cnts_name_template) FAIL(S_ERR_INV_CNTS_NAME_TEMPLATE);
+    memset(printf_arg_types, 0, sizeof(printf_arg_types));
+    printf_arg_count = parse_printf_format(params.cnts_name_template, 10, printf_arg_types);
+    if (printf_arg_count != 0 && printf_arg_count != 1)
+      FAIL(S_ERR_INV_CNTS_NAME_TEMPLATE);
+    if (printf_arg_count == 1 && (printf_arg_types[0] & ~PA_FLAG_MASK) != PA_INT)
+      FAIL(S_ERR_INV_CNTS_NAME_TEMPLATE);
+    for (i = 0, cur_serial = params.first_serial; cur_serial <= params.last_serial; ++i, ++cur_serial) {
+      snprintf(buf, sizeof(buf), params.cnts_name_template, cur_serial);
+      if (strlen(buf) > 1000) FAIL(S_ERR_INV_CNTS_NAME_TEMPLATE);
+      cnts_name_strs[i] = xstrdup(buf);
+    }
+  }
+
+  for (i = 0, cur_serial = params.first_serial; cur_serial <= params.last_serial; ++i, ++cur_serial) {
+    struct userlist_pk_create_user_2 up;
+    memset(&up, 0, sizeof(up));
+    up.random_password_flag = params.reg_random;
+    up.use_sha1_flag = params.reg_sha1;
+    up.is_privileged_flag = params.field_1;
+    up.is_invisible_flag = params.field_2;
+    up.is_banned_flag = params.field_3;
+    up.is_locked_flag = params.field_4;
+    up.show_login_flag = params.field_5;
+    up.show_email_flag = params.field_6;
+    up.read_only_flag = params.field_7;
+    up.never_clean_flag = params.field_8;
+    up.simple_registration_flag = params.field_9;
+    up.contest_id = params.other_contest_id_1;
+    up.cnts_status = params.cnts_status;
+    up.cnts_is_invisible_flag = params.is_invisible;
+    up.cnts_is_banned_flag = params.is_banned;
+    up.cnts_is_locked_flag = params.is_locked;
+    up.cnts_is_incomplete_flag = params.is_incomplete;
+    up.cnts_is_disqualified_flag = params.is_disqualified;
+    up.cnts_use_reg_passwd_flag = params.cnts_use_reg_passwd;
+    up.cnts_set_null_passwd_flag = params.cnts_null_passwd;
+    up.cnts_random_password_flag = params.cnts_random_passwd;
+    up.cnts_use_sha1_flag = params.cnts_sha1;
+    up.group_id = params.other_group_id;
+    other_user_id = 0;
+    r = userlist_clnt_create_user_2(phr->userlist_clnt, ULS_CREATE_USER_2, &up,
+                                    login_strs[i], NULL,
+                                    reg_password_strs[i], cnts_password_strs[i],
+                                    cnts_name_strs[i], &other_user_id);
+    if (r < 0 && r == -ULS_ERR_LOGIN_USED) {
+      FAIL(S_ERR_DUPLICATED_LOGIN);
+    }
+    if (r < 0 || other_user_id <= 0) {
+      FAIL(S_ERR_DB_ERROR);
+    }
+  }
+
+  ss_redirect_2(out_f, phr, SSERV_OP_USER_BROWSE_PAGE, params.contest_id, params.group_id, 0, NULL);
+
+cleanup:
+  if (login_strs) {
+    for (i = 0; i < serial_count; ++i) xfree(login_strs[i]);
+    xfree(login_strs); login_strs = 0;
+  }
+  if (reg_password_strs) {
+    for (i = 0; i < serial_count; ++i) xfree(reg_password_strs[i]);
+    xfree(reg_password_strs); reg_password_strs = 0;
+  }
+  if (cnts_password_strs) {
+    for (i = 0; i < serial_count; ++i) xfree(cnts_password_strs[i]);
+    xfree(cnts_password_strs); cnts_password_strs = 0;
+  }
+  if (cnts_name_strs) {
+    for (i = 0; i < serial_count; ++i) xfree(cnts_name_strs[i]);
+    xfree(cnts_name_strs); cnts_name_strs = 0;
+  }
+  xfree(create_many_sorted_logins); create_many_sorted_logins = 0;
+  xfree(xml_text); xml_text = 0;
+  meta_destroy_fields(&meta_ss_op_param_USER_CREATE_MANY_ACTION_methods, &params);
+  return retval;
+}
+
+int
+super_serve_op_USER_CREATE_FROM_CSV_ACTION(
+        FILE *log_f,
+        FILE *out_f,
+        struct super_http_request_info *phr)
+{
+  int retval = 0, r;
+  opcap_t caps = 0;
+  const struct contest_desc *cnts = 0;
+  unsigned char *xml_text = 0;
+  struct csv_file *csv_parsed = 0;
+  const unsigned char *csv_text = 0;
+  unsigned char *recoded_csv_text = 0;
+  const unsigned char *separator = 0;
+  unsigned char *login_str = 0;
+  unsigned char *email_str = 0;
+  unsigned char *reg_password_str = 0;
+  unsigned char *cnts_password_str = 0;
+  unsigned char *cnts_name_str = 0;
+
+  struct ss_op_param_USER_CREATE_FROM_CSV_ACTION params;
+  memset(&params, 0, sizeof(params));
+  retval = ss_parse_params(phr, &meta_ss_op_param_USER_CREATE_FROM_CSV_ACTION_methods, &params);
+  if (retval < 0) goto cleanup;
+
+  if (params.contest_id > 0) {
+    cnts = 0;
+    if (contests_get(params.contest_id, &cnts) < 0 || !cnts) {
+      params.contest_id = 0;
+    }
+  }
+  cnts = 0;
+  if (params.reg_cnts_create) {
+    if (contests_get(params.other_contest_id_1, &cnts) < 0 || !cnts) {
+      FAIL(S_ERR_INV_CONTEST);
+    }
+  } else {
+    params.other_contest_id_1 = 0;
+  }
+
+  if (params.group_create) {
+    r = userlist_clnt_list_all_users(phr->userlist_clnt, ULS_LIST_GROUP_USERS,
+                                     params.other_group_id, &xml_text);
+    if (r < 0) FAIL(S_ERR_INV_GROUP_ID);
+  } else {
+    params.other_group_id = 0;
+  }
+
+  if (get_global_caps(phr, &caps) < 0 || opcaps_check(caps, OPCAP_CREATE_USER) < 0) {
+    FAIL(S_ERR_PERM_DENIED);
+  }
+  if (cnts) {
+    if (get_contest_caps(phr, cnts, &caps) < 0 || opcaps_check(caps, OPCAP_CREATE_REG) < 0) {
+      FAIL(S_ERR_PERM_DENIED);
+    }
+  }
+
+  if (ss_cgi_param(phr, "csv_file", &csv_text) <= 0 || !csv_text) {
+    FAIL(S_ERR_INV_CSV_FILE);
+  }
+
+  if (params.charset && *params.charset) {
+    int charset_id = charset_get_id(params.charset);
+    if (charset_id < 0) FAIL(S_ERR_INV_CHARSET);
+    if (charset_id > 0) {
+      recoded_csv_text = charset_decode_to_heap(charset_id, csv_text);
+      if (!recoded_csv_text) FAIL(S_ERR_INV_CHARSET);
+      csv_text = recoded_csv_text;
+    }
+  }
+
+  separator = params.separator;
+  if (!separator || !*separator) separator = ";";
+  if (strlen(separator) != 1) FAIL(S_ERR_INV_SEPARATOR);
+  csv_parsed = csv_parse(csv_text, log_f, separator[0]);
+  if (!csv_parsed) FAIL(S_ERR_INV_CSV_FILE);
+
+  if (!csv_parsed->u) {
+    fprintf(log_f, "CSV file is empty\n");
+    FAIL(S_ERR_INV_CSV_FILE);
+  }
+  if (csv_parsed->u > 10000) {
+    fprintf(log_f, "CSV file is too big\n");
+    FAIL(S_ERR_INV_CSV_FILE);
+  }
+
+  // columns: login, email, reg_password (regpassword, password), cnts_password (cntspassword), name (cnts_name, cntsname)
+  int login_idx = -1, email_idx = -1, reg_password_idx = -1, cnts_password_idx = -1, cnts_name_idx = -1;
+  int column_count = csv_parsed->v[0].u;
+  int failed = 0;
+  for (int col = 0; col < column_count; ++col) {
+    unsigned char *txt = fix_string(csv_parsed->v[0].v[col]);
+    if (!txt || !*txt) {
+      fprintf(log_f, "unidentified column %d, skipped\n", col + 1);
+    } else if (!strcasecmp(txt, "login")) {
+      if (login_idx >= 0) {
+        fprintf(log_f, "dupicated column 'login'\n");
+        failed = 1;
+      } else {
+        login_idx = col;
+      }
+    } else if (!strcasecmp(txt, "email")) {
+      if (email_idx >= 0) {
+        fprintf(log_f, "dupicated column 'email'\n");
+        failed = 1;
+      } else {
+        email_idx = col;
+      }
+    } else if (!strcasecmp(txt, "password") || !strcasecmp(txt, "reg_password") || !strcasecmp(txt, "regpassword")) {
+      if (reg_password_idx >= 0) {
+        fprintf(log_f, "dupicated column 'reg_password'\n");
+        failed = 1;
+      } else {
+        reg_password_idx = col;
+      }
+    } else if (!strcasecmp(txt, "cnts_password") || !strcasecmp(txt, "cntspassword")) {
+      if (cnts_password_idx >= 0) {
+        fprintf(log_f, "dupicated column 'cnts_password'\n");
+        failed = 1;
+      } else {
+        cnts_password_idx = col;
+      }
+    } else if (!strcasecmp(txt, "cntsname") || !strcasecmp(txt, "name")) {
+      if (cnts_name_idx >= 0) {
+        fprintf(log_f, "dupicated column 'cnts_name'\n");
+        failed = 1;
+      } else {
+        cnts_name_idx = col;
+      }
+    } else {
+      fprintf(log_f, "unidentified column %d (%s), skipped\n", col + 1, txt);
+    }
+    xfree(txt); txt = 0;
+  }
+  if (login_idx < 0) {
+    fprintf(log_f, "missing column 'login'\n");
+    failed = 1;
+  }
+  if (params.send_email && email_idx < 0) {
+    fprintf(log_f, "missing column 'email'\n");
+    failed = 1;
+  }
+  if (!params.reg_random && reg_password_idx < 0) {
+    fprintf(log_f, "missing column 'reg_password'\n");
+    failed = 1;
+  }
+  if (params.reg_random) {
+    reg_password_idx = -1;
+  }
+  if (cnts && !cnts->disable_team_password && !params.cnts_use_reg_passwd
+      && !params.cnts_null_passwd && !params.cnts_random_passwd
+      && cnts_password_idx < 0) {
+    fprintf(log_f, "missing column 'cnts_password'\n");
+    failed = 1;
+  }
+  if (!cnts || cnts->disable_team_password || params.cnts_use_reg_passwd
+      || params.cnts_null_passwd || params.cnts_random_passwd) {
+    cnts_password_idx = -1;
+  }
+  if (!cnts) {
+    cnts_name_idx = -1;
+  }
+  if (failed) FAIL(S_ERR_INV_CSV_FILE);
+
+  // dry run
+  for (int row = 1; row < csv_parsed->u; ++row) {
+    if (csv_parsed->v[row].u != column_count) {
+      fprintf(log_f, "row %d contains %zu column, but %d columns expected\n",
+              row + 1, csv_parsed->v[row].u, column_count);
+      failed = 1;
+      continue;
+    }
+    unsigned char *txt = fix_string(csv_parsed->v[row].v[login_idx]);
+    int user_id = 0, r = 0;
+    r = userlist_clnt_lookup_user(phr->userlist_clnt, txt, 0, &user_id, NULL);
+    xfree(txt); txt = 0;
+    if (r < 0 && r != -ULS_ERR_INVALID_LOGIN) {
+      FAIL(S_ERR_DB_ERROR);
+    }
+    if (r >= 0 && user_id > 0) {
+      fprintf(log_f, "row %d: login '%s' already exists\n", row + 1, txt);
+      failed = 1;
+    }
+    if (email_idx >= 0) {
+      txt = fix_string(csv_parsed->v[row].v[email_idx]);
+      if (txt && *txt && !is_valid_email_address(txt)) {
+        fprintf(log_f, "row %d: invalid email address\n", row + 1);
+        failed = 1;
+      } else if (params.send_email) {
+        if (!txt || !*txt) {
+          fprintf(log_f, "row %d: email is not specified\n", row + 1);
+          failed = 1;
+        }
+      }
+      xfree(txt); txt = 0;
+    }
+    if (reg_password_idx >= 0) {
+      txt = fix_string(csv_parsed->v[row].v[reg_password_idx]);
+      if (!txt || !*txt) {
+        fprintf(log_f, "row %d: reg_password is not specified\n", row + 1);
+        failed = 1;
+      }
+      xfree(txt); txt = 0;
+    }
+    if (cnts_password_idx >= 0) {
+      txt = fix_string(csv_parsed->v[row].v[cnts_password_idx]);
+      if (!txt || !*txt) {
+        fprintf(log_f, "row %d: cnts_password is not specified\n", row + 1);
+        failed = 1;
+      }
+      xfree(txt); txt = 0;
+    }
+  }
+  if (failed) FAIL(S_ERR_INV_CSV_FILE);
+
+  for (int row = 1; row < csv_parsed->u; ++row) {
+    login_str = 0;
+    email_str = 0;
+    reg_password_str = 0;
+    cnts_password_str = 0;
+    cnts_name_str = 0;
+
+    login_str = fix_string(csv_parsed->v[row].v[login_idx]);
+    if (email_idx >= 0) email_str = fix_string(csv_parsed->v[row].v[email_idx]);
+    if (reg_password_idx >= 0) reg_password_str = fix_string(csv_parsed->v[row].v[reg_password_idx]);
+    if (cnts_password_idx >= 0) cnts_password_str = fix_string(csv_parsed->v[row].v[cnts_password_idx]);
+    if (cnts_name_idx >= 0) cnts_name_str = fix_string(csv_parsed->v[row].v[cnts_name_idx]);
+
+    struct userlist_pk_create_user_2 up;
+    int other_user_id = 0;
+    memset(&up, 0, sizeof(up));
+    up.random_password_flag = params.reg_random;
+    up.use_sha1_flag = params.reg_sha1;
+    up.is_privileged_flag = params.field_1;
+    up.is_invisible_flag = params.field_2;
+    up.is_banned_flag = params.field_3;
+    up.is_locked_flag = params.field_4;
+    up.show_login_flag = params.field_5;
+    up.show_email_flag = params.field_6;
+    up.read_only_flag = params.field_7;
+    up.never_clean_flag = params.field_8;
+    up.simple_registration_flag = params.field_9;
+    up.contest_id = params.other_contest_id_1;
+    up.cnts_status = params.cnts_status;
+    up.cnts_is_invisible_flag = params.is_invisible;
+    up.cnts_is_banned_flag = params.is_banned;
+    up.cnts_is_locked_flag = params.is_locked;
+    up.cnts_is_incomplete_flag = params.is_incomplete;
+    up.cnts_is_disqualified_flag = params.is_disqualified;
+    up.cnts_use_reg_passwd_flag = params.cnts_use_reg_passwd;
+    up.cnts_set_null_passwd_flag = params.cnts_null_passwd;
+    up.cnts_random_password_flag = params.cnts_random_passwd;
+    up.cnts_use_sha1_flag = params.cnts_sha1;
+    up.group_id = params.other_group_id;
+    r = userlist_clnt_create_user_2(phr->userlist_clnt, ULS_CREATE_USER_2, &up,
+                                    login_str, email_str,
+                                    reg_password_str, cnts_password_str,
+                                    cnts_name_str, &other_user_id);
+    if (r < 0 && r == -ULS_ERR_LOGIN_USED) {
+      FAIL(S_ERR_DUPLICATED_LOGIN);
+    }
+    if (r < 0 || other_user_id <= 0) {
+      FAIL(S_ERR_DB_ERROR);
+    }
+
+    xfree(login_str); login_str = 0;
+    xfree(email_str); email_str = 0;
+    xfree(reg_password_str); reg_password_str = 0;
+    xfree(cnts_password_str); cnts_password_str = 0;
+    xfree(cnts_name_str); cnts_name_str = 0;
+  }
+
+  ss_redirect_2(out_f, phr, SSERV_OP_USER_BROWSE_PAGE, params.contest_id, params.group_id, 0, NULL);
+
+cleanup:
+  xfree(login_str); login_str = 0;
+  xfree(email_str); email_str = 0;
+  xfree(reg_password_str); reg_password_str = 0;
+  xfree(cnts_password_str); cnts_password_str = 0;
+  xfree(cnts_name_str); cnts_name_str = 0;
+  csv_parsed = csv_free(csv_parsed);
+  xfree(recoded_csv_text); recoded_csv_text = 0;
+  xfree(xml_text); xml_text = 0;
+  meta_destroy_fields(&meta_ss_op_param_USER_CREATE_FROM_CSV_ACTION_methods, &params);
+  return retval;
+}
+
+int
+super_serve_op_USER_SAVE_ACTION(
+        FILE *log_f,
+        FILE *out_f,
+        struct super_http_request_info *phr)
+{
+  int retval = 0;
+  int contest_id = 0, group_id = 0, other_user_id = 0;
+  const struct contest_desc *cnts = 0;
+  unsigned char *xml_text = 0;
+  struct userlist_user *u = 0;
+  const struct userlist_user_info *ui = 0;
+  unsigned char *other_login_str = 0;
+  unsigned char *email_str = 0;
+  const unsigned char *s = 0;
+  unsigned char param_name[64];
+  opcap_t gcaps = 0;
+  opcap_t caps = 0;
+  int changed_ids[USERLIST_NM_LAST];
+  int deleted_ids[USERLIST_NM_LAST];
+  const unsigned char *changed_strs[USERLIST_NM_LAST];
+  int new_cnts_read_only = 0;
+  int field_id;
+
+  unsigned char *info_fields[USERLIST_NC_LAST];
+  memset(info_fields, 0, sizeof(info_fields));
+
+  unsigned char *member_fields[USERLIST_NM_LAST];
+  memset(member_fields, 0, sizeof(member_fields));
+
+  ss_cgi_param_int_opt(phr, "contest_id", &contest_id, 0);
+  ss_cgi_param_int_opt(phr, "group_id", &group_id, 0);
+  if (ss_cgi_param_int(phr, "other_user_id", &other_user_id) < 0)
+    FAIL(S_ERR_INV_USER_ID);
+  if (contest_id > 0) {
+    if (contests_get(contest_id, &cnts) < 0 || !cnts)
+      FAIL(S_ERR_INV_CONTEST);
+  }
+
+  if (phr->priv_level <= 0) FAIL(S_ERR_PERM_DENIED);
+  get_global_caps(phr, &gcaps);
+  if (cnts) {
+    get_contest_caps(phr, cnts, &caps);
+  } else {
+    caps = gcaps;
+  }
+
+  if (userlist_clnt_get_info(phr->userlist_clnt, ULS_PRIV_GET_USER_INFO,
+                             other_user_id, contest_id, &xml_text) < 0) {
+    FAIL(S_ERR_DB_ERROR);
+  }
+  if (!(u = userlist_parse_user_str(xml_text))) FAIL(S_ERR_DB_ERROR);
+
+  if (ss_cgi_param(phr, "other_login", &s) <= 0 || !s) FAIL(S_ERR_UNSPEC_LOGIN);
+  other_login_str = fix_string(s);
+  if (ss_cgi_param(phr, "email", &s) <= 0) FAIL(S_ERR_INV_VALUE);
+  email_str = fix_string(s);
+  if (email_str && *email_str && !is_valid_email_address(email_str))
+    FAIL(S_ERR_INV_EMAIL);
+
+  static const int global_checkbox_ids[] =
+  {
+    USERLIST_NN_IS_PRIVILEGED,
+    USERLIST_NN_IS_INVISIBLE,
+    USERLIST_NN_IS_BANNED,
+    USERLIST_NN_IS_LOCKED,
+    USERLIST_NN_SHOW_LOGIN,
+    USERLIST_NN_SHOW_EMAIL,
+    USERLIST_NN_READ_ONLY,
+    USERLIST_NN_NEVER_CLEAN,
+    USERLIST_NN_SIMPLE_REGISTRATION,
+    0,
+  };
+  int global_checkbox_vals[USERLIST_NN_LAST];
+  memset(global_checkbox_vals, 0, sizeof(global_checkbox_vals));
+  for (int i = 0; (field_id = global_checkbox_ids[i]); ++i) {
+    snprintf(param_name, sizeof(param_name), "field_%d", field_id);
+    int val = 0;
+    ss_cgi_param_int_opt(phr, param_name, &val, 0);
+    if (val != 1) val = 0;
+    global_checkbox_vals[field_id] = val;
+  }
+
+  int changed_count = 0;
+  if (strcmp(u->login, other_login_str) != 0) {
+    ++changed_count;
+  }
+  if (strcmp(u->email, email_str) != 0) {
+    ++changed_count;
+  }
+  for (int i = 0; global_checkbox_ids[i]; ++i) {
+    const void *ptr = userlist_get_user_field_ptr(u, global_checkbox_ids[i]);
+    if (ptr) {
+      int ival = *(const int*) ptr;
+      if (ival != global_checkbox_vals[i]) {
+        ++changed_count;
+      }
+    }
+  }
+  if (changed_count > 0) {
+    int bit = 0;
+    if (is_globally_privileged(phr, u)
+        || (cnts && is_contest_privileged(cnts, u))
+        || global_checkbox_vals[USERLIST_NN_IS_PRIVILEGED] != u->is_privileged) {
+      bit = OPCAP_PRIV_EDIT_USER;
+    } else {
+      bit = OPCAP_EDIT_USER;
+    }
+    if (opcaps_check(gcaps, bit) < 0) FAIL(S_ERR_PERM_DENIED);
+
+    changed_count = 0;
+    if (strcmp(u->login, other_login_str) != 0) {
+      changed_ids[changed_count] = USERLIST_NN_LOGIN;
+      changed_strs[changed_count] = other_login_str;
+      ++changed_count;
+    }
+    if (strcmp(u->email, email_str) != 0) {
+      changed_ids[changed_count] = USERLIST_NN_EMAIL;
+      changed_strs[changed_count] = email_str;
+      ++changed_count;
+    }
+    for (int i = 0; (field_id = global_checkbox_ids[i]); ++i) {
+      const void *ptr = userlist_get_user_field_ptr(u, field_id);
+      if (ptr) {
+        int ival = *(const int*) ptr;
+        if (ival != global_checkbox_vals[field_id]) {
+          changed_ids[changed_count] = field_id;
+          changed_strs[changed_count] = ival?"1":"0";
+          ++changed_count;
+        }
+      }
+    }
+
+    if (userlist_clnt_edit_field_seq(phr->userlist_clnt, ULS_EDIT_FIELD_SEQ,
+                                     other_user_id, contest_id, 0, 0, changed_count,
+                                     NULL, changed_ids, changed_strs) < 0) {
+      FAIL(S_ERR_DB_ERROR);
+    }
+  }
+
+  static const int info_field_ids[] =
+  {
+    // USERLIST_NC_CNTS_READ_ONLY,
+    USERLIST_NC_NAME,
+    USERLIST_NC_INST,
+    USERLIST_NC_INST_EN,
+    // 105
+    USERLIST_NC_INSTSHORT,
+    USERLIST_NC_INSTSHORT_EN,
+    USERLIST_NC_INSTNUM,
+    USERLIST_NC_FAC,
+    USERLIST_NC_FAC_EN,
+    // 110
+    USERLIST_NC_FACSHORT,
+    USERLIST_NC_FACSHORT_EN,
+    USERLIST_NC_HOMEPAGE,
+    USERLIST_NC_CITY,
+    USERLIST_NC_CITY_EN,
+    // 115
+    USERLIST_NC_COUNTRY,
+    USERLIST_NC_COUNTRY_EN,
+    USERLIST_NC_REGION,
+    USERLIST_NC_AREA,
+    USERLIST_NC_ZIP,
+    // 120
+    USERLIST_NC_STREET,
+    USERLIST_NC_LOCATION,
+    USERLIST_NC_SPELLING,
+    USERLIST_NC_PRINTER_NAME,
+    USERLIST_NC_EXAM_ID,
+    // 125
+    USERLIST_NC_EXAM_CYPHER,
+    USERLIST_NC_LANGUAGES,
+    USERLIST_NC_PHONE,
+    USERLIST_NC_FIELD0,
+    USERLIST_NC_FIELD1,
+    // 130
+    USERLIST_NC_FIELD2,
+    USERLIST_NC_FIELD3,
+    USERLIST_NC_FIELD4,
+    USERLIST_NC_FIELD5,
+    USERLIST_NC_FIELD6,
+    // 135
+    USERLIST_NC_FIELD7,
+    USERLIST_NC_FIELD8,
+    USERLIST_NC_FIELD9,
+    0,
+  };
+  int info_null_fields[USERLIST_NC_LAST];
+  memset(info_null_fields, 0, sizeof(info_null_fields));
+  for (int i = 0; (field_id = info_field_ids[i]); ++i) {
+    snprintf(param_name, sizeof(param_name), "field_null_%d", field_id);
+    int val = 0;
+    ss_cgi_param_int_opt(phr, param_name, &val, 0);
+    if (val != 1) val = 0;
+    info_null_fields[field_id] = val;
+  }
+
+  snprintf(param_name, sizeof(param_name), "field_%d", USERLIST_NC_CNTS_READ_ONLY);
+  ss_cgi_param_int_opt(phr, param_name, &new_cnts_read_only, 0);
+  if (new_cnts_read_only != 1) new_cnts_read_only = 0;
+
+  int cnts_read_only = 0;
+  if (ui && ui->cnts_read_only) cnts_read_only = 1;
+
+  for (int i = 0; (field_id = info_field_ids[i]); ++i) {
+    if (info_null_fields[field_id]) continue;
+    snprintf(param_name, sizeof(param_name), "field_%d", field_id);
+    s = 0;
+    if (ss_cgi_param(phr, param_name, &s) < 0) FAIL(S_ERR_INV_VALUE);
+    if (!s) s = "";
+    info_fields[field_id] = fix_string(s);
+  }
+
+  if (u) ui = u->cnts0;
+  int is_changed = 0;
+  for (int i = 0; (field_id = info_field_ids[i]); ++i) {
+    if (info_null_fields[field_id]) {
+      if (ui && !userlist_is_empty_user_info_field(ui, field_id)) is_changed = 1;
+    } else if (info_fields[field_id]) {
+      if (!ui || !userlist_is_equal_user_info_field(ui, field_id, info_fields[field_id])) is_changed = 1;
+    }
+  }
+
+  int deleted_count = 0;
+  changed_count = 0;
+  if (is_changed) {
+    if (cnts_read_only && new_cnts_read_only) FAIL(S_ERR_DATA_READ_ONLY);
+    if (cnts_read_only != new_cnts_read_only) {
+      changed_ids[changed_count] = USERLIST_NC_CNTS_READ_ONLY;
+      changed_strs[changed_count] = new_cnts_read_only?"1":"0";
+      ++changed_count;
+    }
+    for (int i = 0; (field_id = info_field_ids[i]); ++i) {
+      if (info_null_fields[field_id]) {
+        if (ui && !userlist_is_empty_user_info_field(ui, field_id)) {
+          deleted_ids[deleted_count] = field_id;
+          ++deleted_count;
+        }
+      } else if (info_fields[field_id]) {
+        if (!ui || !userlist_is_equal_user_info_field(ui, field_id, info_fields[field_id])) {
+          changed_ids[changed_count] = field_id;
+          changed_strs[changed_count] = info_fields[field_id];
+          ++changed_count;
+        }
+      }
+    }
+  }
+
+  if (deleted_count > 0 || changed_count > 0) {
+    if (is_globally_privileged(phr, u)) {
+      if (opcaps_check(gcaps, OPCAP_PRIV_EDIT_USER) < 0 || opcaps_check(caps, OPCAP_PRIV_EDIT_USER) < 0)
+        FAIL(S_ERR_PERM_DENIED);
+    } else if (is_contest_privileged(cnts, u)) {
+      if (opcaps_check(caps, OPCAP_PRIV_EDIT_USER) < 0) FAIL(S_ERR_PERM_DENIED);
+    } else {
+      if (opcaps_check(caps, OPCAP_EDIT_USER) < 0) FAIL(S_ERR_PERM_DENIED);
+    }
+
+    if (userlist_clnt_edit_field_seq(phr->userlist_clnt, ULS_EDIT_FIELD_SEQ,
+                                     other_user_id, contest_id, 0, deleted_count, changed_count,
+                                     deleted_ids, changed_ids, changed_strs) < 0) {
+      FAIL(S_ERR_DB_ERROR);
+    }
+  }
+
+  static const int member_field_ids[] = 
+  {
+    USERLIST_NM_STATUS,
+    USERLIST_NM_GENDER,
+    USERLIST_NM_GRADE,
+    USERLIST_NM_FIRSTNAME,
+    /* 205 */
+    USERLIST_NM_FIRSTNAME_EN,
+    USERLIST_NM_MIDDLENAME,
+    USERLIST_NM_MIDDLENAME_EN,
+    USERLIST_NM_SURNAME,
+    USERLIST_NM_SURNAME_EN,
+    /* 210 */
+    USERLIST_NM_GROUP,
+    USERLIST_NM_GROUP_EN,
+    USERLIST_NM_EMAIL,
+    USERLIST_NM_HOMEPAGE,
+    USERLIST_NM_OCCUPATION,
+    /* 215 */
+    USERLIST_NM_OCCUPATION_EN,
+    USERLIST_NM_DISCIPLINE,
+    USERLIST_NM_INST,
+    USERLIST_NM_INST_EN,
+    USERLIST_NM_INSTSHORT,
+    /* 220 */
+    USERLIST_NM_INSTSHORT_EN,
+    USERLIST_NM_FAC,
+    USERLIST_NM_FAC_EN,
+    USERLIST_NM_FACSHORT,
+    USERLIST_NM_FACSHORT_EN,
+    /* 225 */
+    USERLIST_NM_PHONE,
+    USERLIST_NM_CREATE_TIME,
+    USERLIST_NM_LAST_CHANGE_TIME,
+    USERLIST_NM_BIRTH_DATE,
+    USERLIST_NM_ENTRY_DATE,
+    /* 230 */
+    USERLIST_NM_GRADUATION_DATE,
+    0,
+  };
+  int member_null_fields[USERLIST_NM_LAST];
+
+  if (ui && ui->members) {
+    for (int role = 0; role < CONTEST_LAST_MEMBER; ++role) {
+      int role_cnt = userlist_members_count(ui->members, role);
+      if (role_cnt <= 0) continue;
+      for (int pers = 0; pers < role_cnt; ++pers) {
+        const struct userlist_member *m;
+        if (!(m = (const struct userlist_member*) userlist_members_get_nth(ui->members, role, pers)))
+          continue;
+
+        memset(member_null_fields, 0, sizeof(member_null_fields));
+        memset(member_fields, 0, sizeof(member_fields));
+
+        for (int i = 0; (field_id = member_field_ids[i]); ++i) {
+          snprintf(param_name, sizeof(param_name), "field_null_%d_%d", field_id, m->serial);
+          int val = 0;
+          ss_cgi_param_int_opt(phr, param_name, &val, 0);
+          if (val != 1) val = 0;
+          member_null_fields[field_id] = val;
+        }
+
+        for (int i = 0; (field_id = member_field_ids[i]); ++i) {
+          if (member_null_fields[field_id]) continue;
+          snprintf(param_name, sizeof(param_name), "field_%d_%d", field_id, m->serial);
+          int r = ss_cgi_param(phr, param_name, &s);
+          if (!r || !s) continue;
+          if (r < 0) FAIL(S_ERR_INV_VALUE);
+          if (!s) s = "";
+          member_fields[field_id] = fix_string(s);
+        }
+
+        is_changed = 0;
+        for (int i = 0; (field_id = member_field_ids[i]); ++i) {
+          if (member_null_fields[field_id]) {
+            if (!userlist_is_empty_member_field(m, field_id)) is_changed = 1;
+          } else if (member_fields[field_id]) {
+            if (!userlist_is_equal_member_field(m, field_id, member_fields[field_id])) is_changed = 1;
+          }
+        }
+
+        deleted_count = 0;
+        changed_count = 0;
+        if (is_changed) {
+          if (cnts_read_only && new_cnts_read_only) FAIL(S_ERR_DATA_READ_ONLY);
+
+          for (int i = 0; (field_id = member_field_ids[i]); ++i) {
+            if (member_null_fields[field_id]) {
+              if (!userlist_is_empty_member_field(m, field_id)) {
+                deleted_ids[deleted_count] = field_id;
+                ++deleted_count;
+              }
+            } else if (member_fields[field_id]){
+              if (!userlist_is_equal_member_field(m, field_id, member_fields[field_id])) {
+                changed_ids[changed_count] = field_id;
+                changed_strs[changed_count] = member_fields[field_id];
+                ++changed_count;
+              }
+            }
+          }
+        }
+
+        if (deleted_count > 0 || changed_count > 0) {
+          if (is_globally_privileged(phr, u)) {
+            if (opcaps_check(gcaps, OPCAP_PRIV_EDIT_USER) < 0 || opcaps_check(caps, OPCAP_PRIV_EDIT_USER) < 0)
+              FAIL(S_ERR_PERM_DENIED);
+          } else if (is_contest_privileged(cnts, u)) {
+            if (opcaps_check(caps, OPCAP_PRIV_EDIT_USER) < 0) FAIL(S_ERR_PERM_DENIED);
+          } else {
+            if (opcaps_check(caps, OPCAP_EDIT_USER) < 0) FAIL(S_ERR_PERM_DENIED);
+          }
+
+          if (userlist_clnt_edit_field_seq(phr->userlist_clnt, ULS_EDIT_FIELD_SEQ,
+                                           other_user_id, contest_id, m->serial, deleted_count, changed_count,
+                                           deleted_ids, changed_ids, changed_strs) < 0) {
+            FAIL(S_ERR_DB_ERROR);
+          }
+        }
+
+        for (int i = USERLIST_NM_FIRST; i < USERLIST_NM_LAST; ++i) {
+          xfree(member_fields[i]);
+        }
+        memset(member_fields, 0, sizeof(member_fields));
+      }
+    }
+  }
+
+  int next_user_id = 0;
+  int next_op = SSERV_OP_USER_DETAIL_PAGE;
+  if (phr->opcode == SSERV_OP_USER_SAVE_AND_PREV_ACTION) {
+    userlist_clnt_get_prev_user_id(phr->userlist_clnt, ULS_PREV_USER, contest_id, group_id, other_user_id,
+                                   NULL, &next_user_id);
+  } else if (phr->opcode == SSERV_OP_USER_SAVE_AND_NEXT_ACTION) {
+    userlist_clnt_get_prev_user_id(phr->userlist_clnt, ULS_NEXT_USER, contest_id, group_id, other_user_id,
+                                   NULL, &next_user_id);
+  } else {
+    next_op = SSERV_OP_USER_BROWSE_PAGE;
+  }
+  if (next_user_id <= 0) next_op = SSERV_OP_USER_BROWSE_PAGE;
+
+  ss_redirect_2(out_f, phr, next_op, contest_id, group_id, next_user_id, NULL);
+
+cleanup:
+  for (int i = 0; i < USERLIST_NC_LAST; ++i) {
+    xfree(info_fields[i]); info_fields[i] = 0;
+  }
+  for (int i = USERLIST_NM_FIRST; i < USERLIST_NM_LAST; ++i) {
+    xfree(member_fields[i]); member_fields[i] = 0;
+  }
+  xfree(email_str); email_str = 0;
+  xfree(other_login_str); other_login_str = 0;
+  userlist_free(&u->b); u = 0;
+  xfree(xml_text); xml_text = 0;
+  return retval;
+}
+
+int
+super_serve_op_USER_CANCEL_ACTION(
+        FILE *log_f,
+        FILE *out_f,
+        struct super_http_request_info *phr)
+{
+  int retval = 0;
+  int contest_id = 0, group_id = 0, other_user_id = 0;
+  const struct contest_desc *cnts = 0;
+
+  ss_cgi_param_int_opt(phr, "contest_id", &contest_id, 0);
+  ss_cgi_param_int_opt(phr, "group_id", &group_id, 0);
+  ss_cgi_param_int_opt(phr, "other_user_id", &other_user_id, 0);
+  if (contest_id > 0) {
+    if (contests_get(contest_id, &cnts) < 0 || !cnts)
+      FAIL(S_ERR_INV_CONTEST);
+  }
+  if (other_user_id <= 0) phr->opcode = SSERV_OP_USER_CANCEL_ACTION;
+
+  int next_user_id = 0;
+  int next_op = SSERV_OP_USER_DETAIL_PAGE;
+  if (phr->opcode == SSERV_OP_USER_CANCEL_AND_PREV_ACTION) {
+    userlist_clnt_get_prev_user_id(phr->userlist_clnt, ULS_PREV_USER, contest_id, group_id, other_user_id,
+                                   NULL, &next_user_id);
+  } else if (phr->opcode == SSERV_OP_USER_CANCEL_AND_NEXT_ACTION) {
+    userlist_clnt_get_prev_user_id(phr->userlist_clnt, ULS_NEXT_USER, contest_id, group_id, other_user_id,
+                                   NULL, &next_user_id);
+  } else {
+    next_op = SSERV_OP_USER_BROWSE_PAGE;
+  }
+  if (next_user_id <= 0) next_op = SSERV_OP_USER_BROWSE_PAGE;
+
+  ss_redirect_2(out_f, phr, next_op, contest_id, group_id, next_user_id, NULL);
+
+cleanup:
+  return retval;
+}
+
+int
+super_serve_op_USER_CREATE_MEMBER_ACTION(
+        FILE *log_f,
+        FILE *out_f,
+        struct super_http_request_info *phr)
+{
+  int retval = 0;
+  int contest_id = 0, group_id = 0, other_user_id = 0, role = 0;
+  const struct contest_desc *cnts = 0;
+  unsigned char *xml_text = 0;
+  struct userlist_user *u = 0;
+  opcap_t gcaps = 0, caps = 0;
+
+  ss_cgi_param_int_opt(phr, "contest_id", &contest_id, 0);
+  ss_cgi_param_int_opt(phr, "group_id", &group_id, 0);
+  ss_cgi_param_int_opt(phr, "other_user_id", &other_user_id, 0);
+  ss_cgi_param_int_opt(phr, "role", &role, -1);
+
+  if (contest_id <= 0) FAIL(S_ERR_INV_CONTEST);
+  if (contests_get(contest_id, &cnts) < 0 || !cnts) FAIL(S_ERR_INV_CONTEST);
+  if (group_id < 0) group_id = 0;
+
+  if (phr->priv_level <= 0) FAIL(S_ERR_PERM_DENIED);
+  get_global_caps(phr, &gcaps);
+  get_contest_caps(phr, cnts, &caps);
+  caps = (caps | gcaps) & ((1L << OPCAP_EDIT_USER) | (1L << OPCAP_PRIV_EDIT_USER));
+  if (!caps) FAIL(S_ERR_PERM_DENIED);
+
+  if (userlist_clnt_get_info(phr->userlist_clnt, ULS_PRIV_GET_USER_INFO,
+                             other_user_id, contest_id, &xml_text) < 0) {
+    FAIL(S_ERR_DB_ERROR);
+  }
+  if (!(u = userlist_parse_user_str(xml_text))) FAIL(S_ERR_DB_ERROR);
+  --role;
+  if (role < 0 || role >= USERLIST_MB_LAST) FAIL(S_ERR_INV_VALUE);
+
+  if (is_globally_privileged(phr, u)) {
+    if (opcaps_check(gcaps, OPCAP_PRIV_EDIT_USER) < 0) FAIL(S_ERR_PERM_DENIED);
+  } else if (is_contest_privileged(cnts, u)) {
+    if (opcaps_check(caps, OPCAP_PRIV_EDIT_USER) < 0) FAIL(S_ERR_PERM_DENIED);
+  } else {
+    if (opcaps_check(caps, OPCAP_EDIT_USER) < 0) FAIL(S_ERR_PERM_DENIED);
+  }
+
+  int max_count = 0;
+  if (role == CONTEST_M_CONTESTANT) {
+    if (cnts->personal) {
+      max_count = 1;
+    } else if (cnts->members[role]) {
+      max_count = cnts->members[role]->max_count;
+    }
+  } else if (role == CONTEST_M_RESERVE) {
+    if (cnts->personal) {
+      max_count = 0;
+    } else if (cnts->members[role]) {
+      max_count = cnts->members[role]->max_count;
+    }
+  } else {
+    if (cnts->members[role]) {
+      max_count = cnts->members[role]->max_count;
+    }
+  }
+
+  int cur_count = 0;
+  if (u && u->cnts0 && u->cnts0->members) {
+    cur_count = userlist_members_count(u->cnts0->members, role);
+  }
+  if (cur_count >= max_count) FAIL(S_ERR_TOO_MANY_MEMBERS);
+
+  if (userlist_clnt_create_member(phr->userlist_clnt, other_user_id, contest_id, role) < 0)
+    FAIL(S_ERR_DB_ERROR);
+
+  ss_redirect_2(out_f, phr, SSERV_OP_USER_DETAIL_PAGE, contest_id, group_id, other_user_id, NULL);
+
+cleanup:
+  userlist_free(&u->b); u = 0;
+  xfree(xml_text); xml_text = 0;
+  return retval;
+}
+
+int
+super_serve_op_USER_DELETE_MEMBER_PAGE(
+        FILE *log_f,
+        FILE *out_f,
+        struct super_http_request_info *phr)
+{
+  int retval = 0;
+  int other_user_id = 0, contest_id = 0, group_id = 0, serial = 0;
+  const struct contest_desc *cnts = 0;
+  unsigned char contest_id_str[128];
+  unsigned char group_id_str[128];
+  opcap_t gcaps = 0, caps = 0;
+  unsigned char *xml_text = 0;
+  struct userlist_user *u = 0;
+  struct userlist_member *m = 0;
+  int role = 0, num = 0;
+  unsigned char buf[1024];
+  unsigned char hbuf[1024];
+
+  if (ss_cgi_param_int(phr, "other_user_id", &other_user_id) < 0 || other_user_id <= 0) {
+    FAIL(S_ERR_INV_USER_ID);
+  }
+  if (ss_cgi_param_int(phr, "serial", &serial) < 0 || serial <= 0) {
+    FAIL(S_ERR_INV_SERIAL);
+  }
+  if (ss_cgi_param_int(phr, "contest_id", &contest_id) < 0 || contest_id <= 0) {
+    FAIL(S_ERR_INV_CONTEST);
+  }
+  if (contest_id > 0) {
+    if (contests_get(contest_id, &cnts) < 0 || !cnts) {
+      FAIL(S_ERR_INV_CONTEST);
+    }
+  }
+  contest_id_str[0] = 0;
+  if (contest_id > 0) {
+    snprintf(contest_id_str, sizeof(contest_id_str), "&amp;contest_id=%d", contest_id);
+  }
+  ss_cgi_param_int_opt(phr, "group_id", &group_id, 0);
+  if (group_id < 0) group_id = 0;
+  group_id_str[0] = 0;
+  if (group_id > 0) {
+    snprintf(group_id_str, sizeof(group_id_str), "&amp;group_id=%d", group_id);
+  }
+
+  if (phr->priv_level <= 0) FAIL(S_ERR_PERM_DENIED);
+  get_global_caps(phr, &gcaps);
+  get_contest_caps(phr, cnts, &caps);
+  caps = (caps | gcaps) & ((1L << OPCAP_EDIT_USER) | (1L << OPCAP_PRIV_EDIT_USER));
+  if (!caps) FAIL(S_ERR_PERM_DENIED);
+
+  if (userlist_clnt_get_info(phr->userlist_clnt, ULS_PRIV_GET_USER_INFO,
+                             other_user_id, contest_id, &xml_text) < 0) {
+    FAIL(S_ERR_DB_ERROR);
+  }
+  if (!(u = userlist_parse_user_str(xml_text))) FAIL(S_ERR_DB_ERROR);
+
+  if (is_globally_privileged(phr, u)) {
+    if (opcaps_check(gcaps, OPCAP_PRIV_EDIT_USER) < 0) FAIL(S_ERR_PERM_DENIED);
+  } else if (is_contest_privileged(cnts, u)) {
+    if (opcaps_check(caps, OPCAP_PRIV_EDIT_USER) < 0) FAIL(S_ERR_PERM_DENIED);
+  } else {
+    if (opcaps_check(caps, OPCAP_EDIT_USER) < 0) FAIL(S_ERR_PERM_DENIED);
+  }
+
+  m = 0;
+  if (u->cnts0 && u->cnts0->members) {
+    m = userlist_get_member_nc(u->cnts0->members, serial, &role, &num);
+  }
+  if (!m) FAIL(S_ERR_INV_SERIAL);
+  if (role < 0 || role >= USERLIST_MB_LAST || num < 0) FAIL(S_ERR_INV_SERIAL);
+
+  snprintf(buf, sizeof(buf), "serve-control: %s, delete the member '%s'::%d (%d) of user %d, contest %d",
+           phr->html_name, member_string[role], num + 1, serial,
+           other_user_id, contest_id);
+  ss_write_html_header(out_f, phr, buf, 0, NULL);
+  fprintf(out_f, "<h1>%s</h1>\n<br/>\n", buf);
+
+  print_top_navigation_links(log_f, out_f, phr, contest_id, group_id, other_user_id, NULL);
+
+  print_user_info(log_f, out_f, phr, cnts, u, u->cnts0, m, role, num);
+
+  fprintf(out_f, "<p>%s[%s]</a>",
+          html_hyperref(hbuf, sizeof(hbuf), phr->session_id, phr->self_url,
+                        NULL, "action=%d&amp;op=%d&amp;other_user_id=%d%s%s",
+                        SSERV_CMD_HTTP_REQUEST, SSERV_OP_USER_DETAIL_PAGE,
+                        other_user_id, contest_id_str, group_id_str),
+          "Cancel");
+
+  fprintf(out_f, "&nbsp;%s[%s]</a></p>",
+          html_hyperref(hbuf, sizeof(hbuf), phr->session_id, phr->self_url,
+                        NULL, "action=%d&amp;op=%d&amp;other_user_id=%d&amp;serial=%d%s%s",
+                        SSERV_CMD_HTTP_REQUEST, SSERV_OP_USER_DELETE_MEMBER_ACTION,
+                        other_user_id, m->serial, contest_id_str, group_id_str),
+          "Delete");
+
+cleanup:
+  userlist_free(&u->b); u = 0;
+  xfree(xml_text); xml_text = 0;
+  return retval;
+}
+
+int
+super_serve_op_USER_DELETE_MEMBER_ACTION(
+        FILE *log_f,
+        FILE *out_f,
+        struct super_http_request_info *phr)
+{
+  int retval = 0;
+  int contest_id = 0, group_id = 0, other_user_id = 0, serial = 0;
+  const struct contest_desc *cnts = 0;
+  opcap_t gcaps = 0, caps = 0;
+  unsigned char *xml_text = 0;
+  struct userlist_user *u = 0;
+  const struct userlist_member *m = 0;
+
+  ss_cgi_param_int_opt(phr, "contest_id", &contest_id, 0);
+  ss_cgi_param_int_opt(phr, "group_id", &group_id, 0);
+  ss_cgi_param_int_opt(phr, "other_user_id", &other_user_id, 0);
+  ss_cgi_param_int_opt(phr, "serial", &serial, 0);
+
+  if (contest_id <= 0) FAIL(S_ERR_INV_CONTEST);
+  if (contests_get(contest_id, &cnts) < 0 || !cnts) FAIL(S_ERR_INV_CONTEST);
+  if (group_id < 0) group_id = 0;
+
+  if (phr->priv_level <= 0) FAIL(S_ERR_PERM_DENIED);
+  get_global_caps(phr, &gcaps);
+  get_contest_caps(phr, cnts, &caps);
+  caps = (caps | gcaps) & ((1L << OPCAP_EDIT_USER) | (1L << OPCAP_PRIV_EDIT_USER));
+  if (!caps) FAIL(S_ERR_PERM_DENIED);
+
+  if (userlist_clnt_get_info(phr->userlist_clnt, ULS_PRIV_GET_USER_INFO,
+                             other_user_id, contest_id, &xml_text) < 0) {
+    FAIL(S_ERR_DB_ERROR);
+  }
+  if (!(u = userlist_parse_user_str(xml_text))) FAIL(S_ERR_DB_ERROR);
+
+  if (is_globally_privileged(phr, u)) {
+    if (opcaps_check(gcaps, OPCAP_PRIV_EDIT_USER) < 0) FAIL(S_ERR_PERM_DENIED);
+  } else if (is_contest_privileged(cnts, u)) {
+    if (opcaps_check(caps, OPCAP_PRIV_EDIT_USER) < 0) FAIL(S_ERR_PERM_DENIED);
+  } else {
+    if (opcaps_check(caps, OPCAP_EDIT_USER) < 0) FAIL(S_ERR_PERM_DENIED);
+  }
+
+  m = 0;
+  if (u->cnts0 && u->cnts0->members) {
+    m = userlist_get_member_nc(u->cnts0->members, serial, NULL, NULL);
+  }
+  if (!m) FAIL(S_ERR_INV_SERIAL);
+
+  if (userlist_clnt_delete_info(phr->userlist_clnt, ULS_PRIV_DELETE_MEMBER,
+                                other_user_id, contest_id, serial) < 0)
+    FAIL(S_ERR_DB_ERROR);
+
+  ss_redirect_2(out_f, phr, SSERV_OP_USER_DETAIL_PAGE, contest_id, group_id, other_user_id, NULL);
+
+cleanup:
+  userlist_free(&u->b); u = 0;
+  xfree(xml_text); xml_text = 0;
+  return retval;
+}
+
+int
+super_serve_op_USER_CREATE_REG_ACTION(
+        FILE *log_f,
+        FILE *out_f,
+        struct super_http_request_info *phr)
+{
+  int retval = 0;
+  struct userlist_user *u = 0;
+
+  struct ss_op_param_USER_CREATE_REG_ACTION params;
+  memset(&params, 0, sizeof(params));
+  retval = ss_parse_params(phr, &meta_ss_op_param_USER_CREATE_REG_ACTION_methods, &params);
+  if (retval < 0) goto cleanup;
+
+  const struct contest_desc *cnts = 0;
+  if (params.contest_id > 0) {
+    if (contests_get(params.contest_id, &cnts) < 0 || !cnts)
+      params.contest_id = 0;
+  } else {
+    params.contest_id = 0;
+  }
+  cnts = 0;
+  if (params.other_contest_id_1 <= 0) FAIL(S_ERR_INV_CONTEST);
+  if (contests_get(params.other_contest_id_1, &cnts) < 0 || !cnts) FAIL(S_ERR_INV_CONTEST);
+
+  if (params.group_id < 0) params.group_id = 0;
+
+  if (phr->priv_level <= 0) FAIL(S_ERR_PERM_DENIED);
+  opcap_t gcaps = 0, caps = 0;
+  get_global_caps(phr, &gcaps);
+  if (opcaps_check(gcaps, OPCAP_EDIT_USER) >= 0) gcaps |= 1LL << OPCAP_CREATE_REG;
+  if (opcaps_check(gcaps, OPCAP_PRIV_EDIT_USER) >= 0) gcaps |= 1LL << OPCAP_PRIV_CREATE_REG;
+  get_contest_caps(phr, cnts, &caps);
+  caps |= gcaps;
+  if (opcaps_check(caps, OPCAP_CREATE_REG) < 0 && opcaps_check(caps, OPCAP_PRIV_CREATE_REG) < 0)
+    FAIL(S_ERR_PERM_DENIED);
+
+  if (!(u = get_user_info(phr, params.other_user_id, cnts->id))) FAIL(S_ERR_DB_ERROR);
+  if (is_globally_privileged(phr, u)) {
+    if (opcaps_check(gcaps, OPCAP_PRIV_CREATE_REG) < 0) FAIL(S_ERR_PERM_DENIED);
+  } else if (is_contest_privileged(cnts, u)) {
+    if (opcaps_check(caps, OPCAP_PRIV_CREATE_REG) < 0) FAIL(S_ERR_PERM_DENIED);
+  } else {
+    if (opcaps_check(caps, OPCAP_CREATE_REG) < 0) FAIL(S_ERR_PERM_DENIED);
+  }
+
+  if (params.status < 0 || params.status >= USERLIST_REG_LAST) params.status = USERLIST_REG_PENDING;
+
+  int flags = 0;
+  if (params.is_invisible) flags |= USERLIST_UC_INVISIBLE;
+  if (params.is_banned) flags |= USERLIST_UC_BANNED;
+  if (params.is_locked) flags |= USERLIST_UC_LOCKED;
+  if (params.is_incomplete) flags |= USERLIST_UC_INCOMPLETE;
+  if (params.is_disqualified) flags |= USERLIST_UC_DISQUALIFIED;
+
+  if (userlist_clnt_register_contest(phr->userlist_clnt,
+                                     ULS_PRIV_REGISTER_CONTEST,
+                                     params.other_user_id, cnts->id, 0, 0) < 0)
+    FAIL(S_ERR_DB_ERROR);
+  if (userlist_clnt_change_registration(phr->userlist_clnt, params.other_user_id,
+                                        cnts->id, params.status, 4, flags) < 0)
+    FAIL(S_ERR_DB_ERROR);
+
+  ss_redirect_2(out_f, phr, SSERV_OP_USER_DETAIL_PAGE, params.contest_id, params.group_id, params.other_user_id, NULL);
+
+cleanup:
+  meta_destroy_fields(&meta_ss_op_param_USER_CREATE_REG_ACTION_methods, &params);
+  userlist_free(&u->b); u = 0;
+  return retval;
+}
+
+int
+super_serve_op_USER_EDIT_REG_ACTION(
+        FILE *log_f,
+        FILE *out_f,
+        struct super_http_request_info *phr)
+{
+  int retval = 0;
+  struct userlist_user *u = 0;
+
+  struct ss_op_param_USER_EDIT_REG_ACTION params;
+  memset(&params, 0, sizeof(params));
+  retval = ss_parse_params(phr, &meta_ss_op_param_USER_EDIT_REG_ACTION_methods, &params);
+  if (retval < 0) goto cleanup;
+
+  const struct contest_desc *cnts = 0;
+  if (params.contest_id > 0) {
+    if (contests_get(params.contest_id, &cnts) < 0 || !cnts)
+      params.contest_id = 0;
+  } else {
+    params.contest_id = 0;
+  }
+  cnts = 0;
+  if (params.other_contest_id <= 0) FAIL(S_ERR_INV_CONTEST);
+  if (contests_get(params.other_contest_id, &cnts) < 0 || !cnts) FAIL(S_ERR_INV_CONTEST);
+
+  if (params.group_id < 0) params.group_id = 0;
+
+  if (params.next_op != SSERV_OP_USER_DETAIL_PAGE) params.next_op = SSERV_OP_USER_BROWSE_PAGE;
+
+  if (phr->priv_level <= 0) FAIL(S_ERR_PERM_DENIED);
+  opcap_t gcaps = 0, caps = 0;
+  get_global_caps(phr, &gcaps);
+  if (opcaps_check(gcaps, OPCAP_EDIT_USER) >= 0) gcaps |= 1LL << OPCAP_EDIT_REG;
+  if (opcaps_check(gcaps, OPCAP_PRIV_EDIT_USER) >= 0) gcaps |= 1LL << OPCAP_PRIV_EDIT_REG;
+  get_contest_caps(phr, cnts, &caps);
+  caps |= gcaps;
+  if (opcaps_check(caps, OPCAP_CREATE_REG) < 0 && opcaps_check(caps, OPCAP_PRIV_CREATE_REG) < 0)
+    FAIL(S_ERR_PERM_DENIED);
+
+  if (!(u = get_user_info(phr, params.other_user_id, cnts->id))) FAIL(S_ERR_DB_ERROR);
+  if (is_globally_privileged(phr, u)) {
+    if (opcaps_check(gcaps, OPCAP_PRIV_EDIT_REG) < 0) FAIL(S_ERR_PERM_DENIED);
+  } else if (is_contest_privileged(cnts, u)) {
+    if (opcaps_check(caps, OPCAP_PRIV_EDIT_REG) < 0) FAIL(S_ERR_PERM_DENIED);
+  } else {
+    if (opcaps_check(caps, OPCAP_EDIT_REG) < 0) FAIL(S_ERR_PERM_DENIED);
+  }
+
+  if (params.status < 0 || params.status >= USERLIST_REG_LAST) params.status = USERLIST_REG_PENDING;
+
+  int flags = 0;
+  if (params.is_invisible) flags |= USERLIST_UC_INVISIBLE;
+  if (params.is_banned) flags |= USERLIST_UC_BANNED;
+  if (params.is_locked) flags |= USERLIST_UC_LOCKED;
+  if (params.is_incomplete) flags |= USERLIST_UC_INCOMPLETE;
+  if (params.is_disqualified) flags |= USERLIST_UC_DISQUALIFIED;
+
+  if (userlist_clnt_change_registration(phr->userlist_clnt, params.other_user_id,
+                                        cnts->id, params.status, 4, flags) < 0)
+    FAIL(S_ERR_DB_ERROR);
+
+  if (params.next_op == SSERV_OP_USER_DETAIL_PAGE) {
+    ss_redirect_2(out_f, phr, params.next_op, params.contest_id, params.group_id, params.other_user_id, NULL);
+  } else {
+    ss_redirect_2(out_f, phr, params.next_op, params.contest_id, params.group_id, 0, NULL);
+  }
+
+cleanup:
+  meta_destroy_fields(&meta_ss_op_param_USER_EDIT_REG_ACTION_methods, &params);
+  userlist_free(&u->b); u = 0;
+  return retval;
+}
+
+int
+super_serve_op_USER_DELETE_REG_ACTION(
+        FILE *log_f,
+        FILE *out_f,
+        struct super_http_request_info *phr)
+{
+  int retval = 0;
+  struct userlist_user *u = 0;
+  int contest_id = 0, group_id = 0, other_user_id = 0, other_contest_id = 0, next_op = 0;
+  const struct contest_desc *cnts = 0;
+
+  ss_cgi_param_int_opt(phr, "contest_id", &contest_id, 0);
+  ss_cgi_param_int_opt(phr, "group_id", &group_id, 0);
+  ss_cgi_param_int_opt(phr, "other_user_id", &other_user_id, 0);
+  ss_cgi_param_int_opt(phr, "other_contest_id", &other_contest_id, 0);
+  ss_cgi_param_int_opt(phr, "next_op", &next_op, 0);
+
+  if (contest_id < 0) contest_id = 0;
+  if (contest_id > 0) {
+    if (contests_get(contest_id, &cnts) < 0 || !cnts)
+      contest_id = 0;
+  }
+  if (group_id < 0) group_id = 0;
+  if (next_op != SSERV_OP_USER_DETAIL_PAGE) next_op = SSERV_OP_USER_BROWSE_PAGE;
+
+  cnts = 0;
+  if (other_contest_id <= 0) FAIL(S_ERR_INV_CONTEST);
+  if (contests_get(other_contest_id, &cnts) < 0 || !cnts) FAIL(S_ERR_INV_CONTEST);
+
+  if (phr->priv_level <= 0) FAIL(S_ERR_PERM_DENIED);
+  opcap_t gcaps = 0, caps = 0;
+  get_global_caps(phr, &gcaps);
+  if (opcaps_check(gcaps, OPCAP_EDIT_USER) >= 0) gcaps |= 1LL << OPCAP_DELETE_REG;
+  if (opcaps_check(gcaps, OPCAP_PRIV_EDIT_USER) >= 0) gcaps |= 1LL << OPCAP_PRIV_DELETE_REG;
+  get_contest_caps(phr, cnts, &caps);
+  caps |= gcaps;
+  if (opcaps_check(caps, OPCAP_PRIV_DELETE_REG) < 0 && opcaps_check(caps, OPCAP_DELETE_REG) < 0)
+    FAIL(S_ERR_PERM_DENIED);
+
+  if (!(u = get_user_info(phr, other_user_id, cnts->id))) FAIL(S_ERR_DB_ERROR);
+  if (is_globally_privileged(phr, u)) {
+    if (opcaps_check(gcaps, OPCAP_PRIV_DELETE_REG) < 0) FAIL(S_ERR_PERM_DENIED);
+  } else if (is_contest_privileged(cnts, u)) {
+    if (opcaps_check(caps, OPCAP_PRIV_DELETE_REG) < 0) FAIL(S_ERR_PERM_DENIED);
+  } else {
+    if (opcaps_check(caps, OPCAP_DELETE_REG) < 0) FAIL(S_ERR_PERM_DENIED);
+  }
+
+  if (userlist_clnt_change_registration(phr->userlist_clnt, u->id, cnts->id, -2, 0, 0) < 0)
+    FAIL(S_ERR_DB_ERROR);
+
+  if (next_op == SSERV_OP_USER_DETAIL_PAGE) {
+    ss_redirect_2(out_f, phr, next_op, contest_id, group_id, other_user_id, NULL);
+  } else {
+    ss_redirect_2(out_f, phr, next_op, contest_id, group_id, 0, NULL);
+  }
+
+cleanup:
+  userlist_free(&u->b); u = 0;
+  return retval;
+}
+
+static const unsigned char clearable_fields[] =
+{
+  [USERLIST_NN_IS_PRIVILEGED] = 1,
+  [USERLIST_NN_IS_INVISIBLE] = 1,
+  [USERLIST_NN_IS_BANNED] = 1,
+  [USERLIST_NN_IS_LOCKED] = 1,
+  [USERLIST_NN_SHOW_LOGIN] = 1,
+  [USERLIST_NN_SHOW_EMAIL] = 1,
+  [USERLIST_NN_READ_ONLY] = 1,
+  [USERLIST_NN_NEVER_CLEAN] = 1,
+  [USERLIST_NN_SIMPLE_REGISTRATION] = 1,
+  [USERLIST_NN_EMAIL] = 1,
+
+  [USERLIST_NC_CNTS_READ_ONLY] = 1,
+  [USERLIST_NC_NAME] = 1,
+  [USERLIST_NC_INST] = 1,
+  [USERLIST_NC_INST_EN] = 1,
+  [USERLIST_NC_INSTSHORT] = 1,
+  [USERLIST_NC_INSTSHORT_EN] = 1,
+  [USERLIST_NC_INSTNUM] = 1,
+  [USERLIST_NC_FAC] = 1,
+  [USERLIST_NC_FAC_EN] = 1,
+  [USERLIST_NC_FACSHORT] = 1,
+  [USERLIST_NC_FACSHORT_EN] = 1,
+  [USERLIST_NC_HOMEPAGE] = 1,
+  [USERLIST_NC_CITY] = 1,
+  [USERLIST_NC_CITY_EN] = 1,
+  [USERLIST_NC_COUNTRY] = 1,
+  [USERLIST_NC_COUNTRY_EN] = 1,
+  [USERLIST_NC_REGION] = 1,
+  [USERLIST_NC_AREA] = 1,
+  [USERLIST_NC_ZIP] = 1,
+  [USERLIST_NC_STREET] = 1,
+  [USERLIST_NC_LOCATION] = 1,
+  [USERLIST_NC_SPELLING] = 1,
+  [USERLIST_NC_PRINTER_NAME] = 1,
+  [USERLIST_NC_EXAM_ID] = 1,
+  [USERLIST_NC_EXAM_CYPHER] = 1,
+  [USERLIST_NC_LANGUAGES] = 1,
+  [USERLIST_NC_PHONE] = 1,
+  [USERLIST_NC_FIELD0] = 1,
+  [USERLIST_NC_FIELD1] = 1,
+  [USERLIST_NC_FIELD2] = 1,
+  [USERLIST_NC_FIELD3] = 1,
+  [USERLIST_NC_FIELD4] = 1,
+  [USERLIST_NC_FIELD5] = 1,
+  [USERLIST_NC_FIELD6] = 1,
+  [USERLIST_NC_FIELD7] = 1,
+  [USERLIST_NC_FIELD8] = 1,
+  [USERLIST_NC_FIELD9] = 1,
+
+  [USERLIST_NM_STATUS] = 1,
+  [USERLIST_NM_GENDER] = 1,
+  [USERLIST_NM_GRADE] = 1,
+  [USERLIST_NM_FIRSTNAME] = 1,
+  [USERLIST_NM_FIRSTNAME_EN] = 1,
+  [USERLIST_NM_MIDDLENAME] = 1,
+  [USERLIST_NM_MIDDLENAME_EN] = 1,
+  [USERLIST_NM_SURNAME] = 1,
+  [USERLIST_NM_SURNAME_EN] = 1,
+  [USERLIST_NM_GROUP] = 1,
+  [USERLIST_NM_GROUP_EN] = 1,
+  [USERLIST_NM_EMAIL] = 1,
+  [USERLIST_NM_HOMEPAGE] = 1,
+  [USERLIST_NM_OCCUPATION] = 1,
+  [USERLIST_NM_OCCUPATION_EN] = 1,
+  [USERLIST_NM_DISCIPLINE] = 1,
+  [USERLIST_NM_INST] = 1,
+  [USERLIST_NM_INST_EN] = 1,
+  [USERLIST_NM_INSTSHORT] = 1,
+  [USERLIST_NM_INSTSHORT_EN] = 1,
+  [USERLIST_NM_FAC] = 1,
+  [USERLIST_NM_FAC_EN] = 1,
+  [USERLIST_NM_FACSHORT] = 1,
+  [USERLIST_NM_FACSHORT_EN] = 1,
+  [USERLIST_NM_PHONE] = 1,
+  [USERLIST_NM_BIRTH_DATE] = 1,
+  [USERLIST_NM_ENTRY_DATE] = 1,
+  [USERLIST_NM_GRADUATION_DATE] = 1,
+};
+
+int
+super_serve_op_USER_CLEAR_FIELD_ACTION(
+        FILE *log_f,
+        FILE *out_f,
+        struct super_http_request_info *phr)
+{
+  int retval = 0;
+  int contest_id = 0, group_id = 0, other_user_id = 0, field_id = 0, member_id = 0;
+  const struct contest_desc *cnts = 0;
+  struct userlist_user *u = 0;
+  const struct userlist_member *m = 0;
+
+  ss_cgi_param_int_opt(phr, "contest_id", &contest_id, 0);
+  ss_cgi_param_int_opt(phr, "group_id", &group_id, 0);
+  ss_cgi_param_int_opt(phr, "other_user_id", &other_user_id, 0);
+  ss_cgi_param_int_opt(phr, "field_id", &field_id, 0);
+  ss_cgi_param_int_opt(phr, "member_id", &member_id, 0);
+
+  if (contest_id <= 0) FAIL(S_ERR_INV_CONTEST);
+  if (contests_get(contest_id, &cnts) < 0 || !cnts) FAIL(S_ERR_INV_CONTEST);
+  if (group_id < 0) group_id = 0;
+
+  if (phr->priv_level <= 0) FAIL(S_ERR_PERM_DENIED);
+  opcap_t gcaps = 0, caps = 0;
+  get_global_caps(phr, &gcaps);
+  get_contest_caps(phr, cnts, &caps);
+  caps |= gcaps;
+  if (opcaps_check(caps, OPCAP_PRIV_EDIT_USER) < 0 && opcaps_check(caps, OPCAP_EDIT_USER) < 0)
+    FAIL(S_ERR_PERM_DENIED);
+
+  if (!(u = get_user_info(phr, other_user_id, cnts->id))) FAIL(S_ERR_DB_ERROR);
+  if (is_globally_privileged(phr, u)) {
+    if (opcaps_check(gcaps, OPCAP_PRIV_EDIT_USER) < 0) FAIL(S_ERR_PERM_DENIED);
+  } else if (is_contest_privileged(cnts, u)) {
+    if (opcaps_check(caps, OPCAP_PRIV_EDIT_USER) < 0) FAIL(S_ERR_PERM_DENIED);
+  } else {
+    if (opcaps_check(caps, OPCAP_EDIT_USER) < 0) FAIL(S_ERR_PERM_DENIED);
+  }
+
+  if (field_id < USERLIST_NN_FIRST || field_id >= USERLIST_NM_LAST)
+    FAIL(S_ERR_INV_VALUE);
+  if (!clearable_fields[field_id])
+    FAIL(S_ERR_INV_VALUE);
+
+  if (field_id >= USERLIST_NM_FIRST && field_id < USERLIST_NM_LAST) {
+    if (u->cnts0 && u->cnts0->members) {
+      m = userlist_get_member_nc(u->cnts0->members, field_id, NULL, NULL);
+    }
+    if (!m) {
+      member_id = 0;
+    }
+  } else {
+    member_id = 0;
+  }
+
+  if (userlist_clnt_delete_field(phr->userlist_clnt, ULS_DELETE_FIELD,
+                                 other_user_id, contest_id, member_id,
+                                 field_id) < 0) {
+    FAIL(S_ERR_DB_ERROR);
+  }
+
+  ss_redirect_2(out_f, phr, SSERV_OP_USER_DETAIL_PAGE, contest_id, group_id, other_user_id, NULL);
+
+cleanup:
+  userlist_free(&u->b); u = 0;
+  return retval;
+}
+
+int
+super_serve_op_USER_SEL_VIEW_PASSWD_PAGE(
+        FILE *log_f,
+        FILE *out_f,
+        struct super_http_request_info *phr)
+{
+  int retval = 0, r = 0;
+  int contest_id = 0, group_id = 0;
+  unsigned char *marked_str = 0;
+  bitset_t marked = BITSET_INITIALIZER;
+  const struct contest_desc *cnts = 0;
+  opcap_t gcaps = 0, caps = 0;
+  unsigned char *xml_text = 0;
+  struct userlist_list *users = 0;
+  unsigned char buf[1024];
+  const unsigned char *s = 0;
+  const unsigned char *cl = 0;
+  struct html_armor_buffer ab = HTML_ARMOR_INITIALIZER;
+  int user_id = 0, serial;
+  const struct userlist_user *u = 0;
+  const struct userlist_contest *reg = 0;
+  const struct userlist_user_info *ui = 0;
+  int allowed, passwd_method;
+  const unsigned char *passwd;
+
+  ss_cgi_param_int_opt(phr, "contest_id", &contest_id, 0);
+  ss_cgi_param_int_opt(phr, "group_id", &group_id, 0);
+  marked_str = collect_marked_set(phr, &marked);
+
+  if (contest_id < 0) contest_id = 0;
+  if (contest_id > 0) {
+    if (contests_get(contest_id, &cnts) < 0 || !cnts) contest_id = 0;
+  }
+  if (group_id < 0) group_id = 0;
+
+  /* check permissions */
+  switch (phr->opcode) {
+  case SSERV_OP_USER_SEL_VIEW_PASSWD_PAGE:
+    get_global_caps(phr, &gcaps);
+    if (cnts) get_contest_caps(phr, cnts, &caps);
+    caps |= gcaps;
+    break;
+  case SSERV_OP_USER_SEL_VIEW_CNTS_PASSWD_PAGE:
+    if (!cnts) FAIL(S_ERR_INV_CONTEST);
+    if (cnts->disable_team_password) FAIL(S_ERR_INV_CONTEST);
+    get_global_caps(phr, &gcaps);
+    get_contest_caps(phr, cnts, &caps);
+    caps |= gcaps;
+    break;
+  default:
+    abort();
+  }
+  if (opcaps_check(caps, OPCAP_GET_USER) < 0) FAIL(S_ERR_PERM_DENIED);
+  if (opcaps_check(caps, OPCAP_PRIV_EDIT_PASSWD) < 0 && opcaps_check(caps, OPCAP_EDIT_PASSWD) < 0) FAIL(S_ERR_PERM_DENIED);
+
+  if (!phr->userlist_clnt) FAIL(S_ERR_DB_ERROR);
+  r = userlist_clnt_list_users_2(phr->userlist_clnt, ULS_LIST_ALL_USERS_4,
+                                 contest_id, group_id, marked_str, 0, 0,
+                                 &xml_text);
+
+  if (r < 0) FAIL(S_ERR_DB_ERROR);
+  users = userlist_parse_str(xml_text);
+  if (!users) FAIL(S_ERR_DB_ERROR);
+
+  switch (phr->opcode) {
+  case SSERV_OP_USER_SEL_VIEW_PASSWD_PAGE:
+    snprintf(buf, sizeof(buf), "serve-control: %s, view registration passwords", phr->html_name);
+    break;
+  case SSERV_OP_USER_SEL_VIEW_CNTS_PASSWD_PAGE:
+    snprintf(buf, sizeof(buf), "serve-control: %s, view contest passwords in contest %d", phr->html_name, contest_id);
+    break;
+  default:
+    abort();
+  }
+
+  ss_write_html_header(out_f, phr, buf, 0, NULL);
+
+  fprintf(out_f, "<h1>%s</h1>\n", buf);
+  if (cnts && cnts->name) {
+    fprintf(out_f, "<h2>Contest %d: %s</h2>\n", cnts->id, ARMOR(cnts->name));
+  } else {
+    fprintf(out_f, "<br/>\n");
+  }
+
+  print_top_navigation_links(log_f, out_f, phr, contest_id, group_id, 0, marked_str);
+
+  cl = " class=\"b1\"";
+  fprintf(out_f, "<table%s>", cl);
+  fprintf(out_f, "<tr>");
+  fprintf(out_f, "<th%s>%s</th><th%s>%s</th><th%s>%s</th>",
+          cl, "NN", cl, "User ID", cl, "Login");
+  s = "Registration password";
+  if (phr->opcode == SSERV_OP_USER_SEL_VIEW_CNTS_PASSWD_PAGE) s = "Contest password";
+  fprintf(out_f, "<th%s>%s</th>", cl, s);
+  if (cnts) {
+    fprintf(out_f, "<th%s>%s</th><th%s>%s</th><th%s>%s</th><th%s>%s</th><th%s>%s</th>",
+            cl, "Name", cl, "Status", cl, "Flags", cl, "Location", cl, "Printer name");
+  }
+  fprintf(out_f, "</tr>\n");
+  for (user_id = 1, serial = 0; user_id < marked.size; ++user_id) {
+    if (!bitset_get(&marked, user_id)) continue;
+    if (user_id >= users->user_map_size) continue;
+    if (!(u = users->user_map[user_id])) continue;
+    ui = u->cnts0;
+    reg = 0;
+    if (cnts) {
+      reg = userlist_get_user_contest(u, contest_id);
+      if (!reg) continue;
+    }
+    fprintf(out_f, "<tr><td%s>%d</td>", cl, ++serial);
+    fprintf(out_f, "<td%s>%d</td>", cl, user_id);
+    fprintf(out_f, "<td%s>%s</td>", cl, ARMOR(u->login));
+    fprintf(out_f, "<td%s>", cl);
+    allowed = 0;
+    passwd_method = -1;
+    passwd = 0;
+    switch (phr->opcode) {
+    case SSERV_OP_USER_SEL_VIEW_PASSWD_PAGE:
+      if (is_globally_privileged(phr, u)) {
+        if (opcaps_check(gcaps, OPCAP_PRIV_EDIT_PASSWD) >= 0) allowed = 1;
+      } else if (cnts && is_contest_privileged(cnts, u)) {
+        if (opcaps_check(caps, OPCAP_PRIV_EDIT_PASSWD) >= 0) allowed = 1;
+      } else {
+        if (opcaps_check(caps, OPCAP_EDIT_PASSWD) >= 0) allowed = 1;
+      }
+      if (allowed) {
+        passwd_method = u->passwd_method;
+        passwd = u->passwd;
+      }
+      break;
+    case SSERV_OP_USER_SEL_VIEW_CNTS_PASSWD_PAGE:
+      if (is_globally_privileged(phr, u)) {
+        if (opcaps_check(gcaps, OPCAP_PRIV_EDIT_PASSWD) >= 0) allowed = 1;
+      } else if (is_contest_privileged(cnts, u)) {
+        if (opcaps_check(caps, OPCAP_PRIV_EDIT_PASSWD) >= 0) allowed = 1;
+      } else {
+        if (opcaps_check(caps, OPCAP_EDIT_PASSWD) >= 0) allowed = 1;
+      }
+      if (allowed && ui) {
+        passwd_method = ui->team_passwd_method;
+        passwd = ui->team_passwd;
+      }
+      break;
+    default:
+      break;
+    }
+    if (!allowed) {
+      fprintf(out_f, "<i>hidden</i>");
+    } else if (passwd_method < 0 || !passwd) {
+      fprintf(out_f, "<i>null</i>");
+    } else if (passwd_method == USERLIST_PWD_SHA1) {
+      fprintf(out_f, "<i>changed</i>");
+    } else if (passwd_method == USERLIST_PWD_PLAIN) {
+      fprintf(out_f, "<tt>%s</tt>", ARMOR(passwd));
+    } else {
+      fprintf(out_f, "<i>unknown</i>");
+    }
+    fprintf(out_f, "</td>");
+    if (cnts) {
+      s = u->login;
+      if (ui && ui->name && *ui->name) s = ui->name;
+      fprintf(out_f, "<td%s>%s</td>", cl, ARMOR(s));
+      if (cnts && reg) {
+        r = reg->status;
+        if (r < 0 || r >= USERLIST_REG_LAST) r = USERLIST_REG_LAST;
+        fprintf(out_f, "<td%s>%s</td>", cl, reg_status_strs[r]);
+      } else {
+        fprintf(out_f, "<td%s>&nbsp;</td>", cl);
+      }
+      fprintf(out_f, "<td%s>", cl);
+      s = "";
+      if (is_privileged(phr, cnts, u)) {
+        fprintf(out_f, "%s%s", s, "privileged");
+        s = ", ";
+      }
+      if (cnts && reg) {
+        if ((reg->flags & USERLIST_UC_INVISIBLE)) {
+          fprintf(out_f, "%s%s", s, "invisible");
+          s = ", ";
+        }
+        if ((reg->flags & USERLIST_UC_BANNED)) {
+          fprintf(out_f, "%s%s", s, "banned");
+          s = ", ";
+        }
+        if ((reg->flags & USERLIST_UC_LOCKED)) {
+          fprintf(out_f, "%s%s", s, "locked");
+          s = ", ";
+        }
+        if ((reg->flags & USERLIST_UC_DISQUALIFIED)) {
+          fprintf(out_f, "%s%s", s, "disqualified");
+          s = ", ";
+        }
+      }
+      if (!*s) fprintf(out_f, "&nbsp;");
+      fprintf(out_f, "</td>");
+      s = "";
+      if (ui && ui->location) s = ui->location;
+      fprintf(out_f, "<td%s>%s</td>", cl, ARMOR(s));
+      s = "";
+      if (ui && ui->printer_name) s = ui->printer_name;
+      fprintf(out_f, "<td%s>%s</td>", cl, ARMOR(s));
+    }
+    fprintf(out_f, "</tr>\n");
+  }
+  fprintf(out_f, "</table>\n");
+
+  ss_write_html_footer(out_f);
+
+cleanup:
+  html_armor_free(&ab);
+  userlist_free(&users->b); users = 0;
+  xfree(xml_text);
+  bitset_free(&marked);
+  xfree(marked_str);
+  return retval;
+}
+
+int
+super_serve_op_USER_SEL_VIEW_PASSWD_REDIRECT(
+        FILE *log_f,
+        FILE *out_f,
+        struct super_http_request_info *phr)
+{
+  int contest_id = 0, group_id = 0, next_op = 0;
+  bitset_t marked = BITSET_INITIALIZER;
+  unsigned char *marked_str = 0;
+
+  ss_cgi_param_int_opt(phr, "contest_id", &contest_id, 0);
+  ss_cgi_param_int_opt(phr, "group_id", &group_id, 0);
+  marked_str = collect_marked_set(phr, &marked);
+  switch (phr->opcode) {
+  case SSERV_OP_USER_SEL_VIEW_PASSWD_REDIRECT:
+    next_op = SSERV_OP_USER_SEL_VIEW_PASSWD_PAGE;
+    break;
+  case SSERV_OP_USER_SEL_VIEW_CNTS_PASSWD_REDIRECT:
+    next_op = SSERV_OP_USER_SEL_VIEW_CNTS_PASSWD_PAGE;
+    break;
+  default:
+    abort();
+  }
+  ss_redirect_2(out_f, phr, next_op, contest_id, group_id, 0, marked_str);
+
+  xfree(marked_str);
+  bitset_free(&marked);
+  return 0;
+}
+
+int
+super_serve_op_GROUP_BROWSE_PAGE(
+        FILE *log_f,
+        FILE *out_f,
+        struct super_http_request_info *phr)
+{
+  int retval = 0, r;
+  const struct userlist_group *g;
+  const unsigned char *cl;
+  int min_group_id = INT_MAX;
+  int max_group_id = 0;
+  int group_id, serial;
+  struct userlist_list *users = 0;
+  unsigned char *xml_text = 0;
+  unsigned char hbuf[1024];
+  unsigned char buf[1024];
+  const unsigned char *group_filter = 0;
+  int group_offset = 0;
+  int group_count = 20;
+  const unsigned char *s;
+  opcap_t gcaps = 0;
+  struct html_armor_buffer ab = HTML_ARMOR_INITIALIZER;
+
+  if (get_global_caps(phr, &gcaps) < 0 && opcaps_check(gcaps, OPCAP_LIST_USERS) < 0) {
+    FAIL(S_ERR_PERM_DENIED);
+  }
+
+  snprintf(buf, sizeof(buf), "serve-control: %s, browsing groups", phr->html_name);
+  ss_write_html_header(out_f, phr, buf, 0, NULL);
+  fprintf(out_f, "<h1>%s</h1>\n", buf);
+
+  print_top_navigation_links(log_f, out_f, phr, 0, 0, 0, NULL);
+
+  if (!phr->userlist_clnt) {
+    fprintf(out_f, "<hr/><h2>Error</h2>\n");
+    fprintf(out_f, "<pre>No connection to the server!</pre>\n");
+    goto do_footer;
+  }
+
+  if (phr->ss->group_filter_set) {
+    group_filter = phr->ss->group_filter;
+    group_offset = phr->ss->group_offset;
+    group_count = phr->ss->group_count;
+  }
+
+  html_start_form(out_f, 1, phr->self_url, "");
+  html_hidden(out_f, "SID", "%016llx", phr->session_id);
+  html_hidden(out_f, "action", "%d", SSERV_CMD_HTTP_REQUEST);
+  fprintf(out_f, "<table class=\"b0\">");
+  s = group_filter;
+  if (!s) s = "";
+  fprintf(out_f, "<!--<tr><td class=\"b0\">Filter:</td><td class=\"b0\">%s</td></tr>-->",
+          html_input_text(buf, sizeof(buf), "group_filter", 50, "%s", ARMOR(s)));
+  hbuf[0] = 0;
+  if (phr->ss->group_filter_set) {
+    snprintf(hbuf, sizeof(hbuf), "%d", group_offset);
+  }
+  fprintf(out_f, "<tr><td class=\"b0\">Offset:</td><td class=\"b0\">%s</td></tr>",
+          html_input_text(buf, sizeof(buf), "group_offset", 10, "%s", hbuf));
+  hbuf[0] = 0;
+  if (phr->ss->group_filter_set) {
+    snprintf(hbuf, sizeof(hbuf), "%d", group_count);
+  }
+  fprintf(out_f, "<tr><td class=\"b0\">Count:</td><td class=\"b0\">%s</td></tr>",
+          html_input_text(buf, sizeof(buf), "group_count", 10, "%s", hbuf));
+  fprintf(out_f, "<tr><td class=\"b0\">&nbsp;</td><td class=\"b0\"><input type=\"submit\" name=\"op_%d\" value=\"%s\" /></td></tr>",
+          SSERV_OP_GROUP_FILTER_CHANGE_ACTION, "Change");
+  fprintf(out_f, "</table>");
+  fprintf(out_f, "<table class=\"b0\"><tr>");
+  fprintf(out_f, "<td class=\"b0\"><input type=\"submit\" name=\"op_%d\" value=\"%s\" /></td>", SSERV_OP_GROUP_FILTER_FIRST_PAGE_ACTION, "&lt;&lt;");
+  fprintf(out_f, "<td class=\"b0\"><input type=\"submit\" name=\"op_%d\" value=\"%s\" /></td>", SSERV_OP_GROUP_FILTER_PREV_PAGE_ACTION, "&lt;");
+  fprintf(out_f, "<td class=\"b0\"><input type=\"submit\" name=\"op_%d\" value=\"%s\" /></td>", SSERV_OP_GROUP_FILTER_NEXT_PAGE_ACTION, "&gt;");
+  fprintf(out_f, "<td class=\"b0\"><input type=\"submit\" name=\"op_%d\" value=\"%s\" /></td>", SSERV_OP_GROUP_FILTER_LAST_PAGE_ACTION, "&gt;&gt;");
+  fprintf(out_f, "</tr></table>\n");
+
+  r = userlist_clnt_list_users_2(phr->userlist_clnt, ULS_LIST_ALL_GROUPS_2,
+                                 0, 0, group_filter, group_offset, group_count,
+                                 &xml_text);
+  if (r < 0) {
+    fprintf(out_f, "</form>\n");
+    fprintf(out_f, "<hr/><h2>Error</h2>\n");
+    fprintf(out_f, "<pre>Cannot get user list: %s</pre>\n",
+            userlist_strerror(-r));
+    goto do_footer;
+  }
+  users = userlist_parse_str(xml_text);
+  if (!users) {
+    fprintf(out_f, "</form>\n");
+    fprintf(out_f, "<hr/><h2>Error</h2>\n");
+    fprintf(out_f, "<pre>XML parse error</pre>\n");
+    goto do_footer;
+  }
+
+  for (group_id = 1; group_id < users->group_map_size; ++group_id) {
+    if (!(g = users->group_map[group_id])) continue;
+    if (group_id >= max_group_id) max_group_id = group_id;
+    if (group_id <= min_group_id) min_group_id = group_id;
+  }
+  html_hidden(out_f, "min_group_id", "%d", min_group_id);
+  html_hidden(out_f, "max_group_id", "%d", max_group_id);
+
+  cl = " class=\"b1\"";
+  fprintf(out_f, "<table%s>\n", cl);
+  fprintf(out_f, "<tr>");
+  fprintf(out_f, "<th%s>NN</th>", cl);
+  fprintf(out_f, "<th%s>Group Id</th>", cl);
+  fprintf(out_f, "<th%s>Group Name</th>", cl);
+  fprintf(out_f, "<th%s>Description</th>", cl);
+  fprintf(out_f, "<th%s>Operations</th>", cl);
+  fprintf(out_f, "</tr>\n");
+
+  serial = group_offset - 1;
+  for (group_id = 1; group_id < users->group_map_size; ++group_id) {
+    if (!(g = users->group_map[group_id])) continue;
+
+    ++serial;
+    fprintf(out_f, "<tr>\n");
+    fprintf(out_f, "<td class=\"b1\">%d</td>", serial);
+    fprintf(out_f, "<td class=\"b1\">%d</td>", group_id);
+    if (!g->group_name) {
+      fprintf(out_f, "<td class=\"b1\"><i>NULL</i></td>");
+    } else {
+      fprintf(out_f, "<td class=\"b1\"><tt>%s</tt></td>", ARMOR(g->group_name));
+    }
+    if (!g->description) {
+      fprintf(out_f, "<td class=\"b1\"><i>NULL</i></td>");
+    } else {
+      fprintf(out_f, "<td class=\"b1\"><tt>%s</tt></td>", ARMOR(g->description));
+    }
+
+    fprintf(out_f, "<td%s>", cl);
+    fprintf(out_f, "%s%s</a>",
+            html_hyperref(hbuf, sizeof(hbuf), phr->session_id, phr->self_url,
+                          NULL, "action=%d&amp;op=%d&amp;group_id=%d",
+                          SSERV_CMD_HTTP_REQUEST, SSERV_OP_GROUP_MODIFY_PAGE,
+                          group_id),
+            "[Modify]");
+    fprintf(out_f, "&nbsp;%s%s</a>",
+            html_hyperref(hbuf, sizeof(hbuf), phr->session_id, phr->self_url,
+                          NULL, "action=%d&amp;op=%d&amp;group_id=%d",
+                          SSERV_CMD_HTTP_REQUEST, SSERV_OP_USER_BROWSE_PAGE,
+                          group_id),
+            "[Members]");
+    fprintf(out_f, "&nbsp;%s%s</a>",
+            html_hyperref(hbuf, sizeof(hbuf), phr->session_id, phr->self_url,
+                          NULL, "action=%d&amp;op=%d&amp;group_id=%d",
+                          SSERV_CMD_HTTP_REQUEST, SSERV_OP_GROUP_DELETE_PAGE,
+                          group_id),
+            "[Delete]");
+    fprintf(out_f, "</td>");
+    fprintf(out_f, "</tr>\n");
+
+  }
+  fprintf(out_f, "</table>\n");
+  fprintf(out_f, "</form>\n");
+
+  if (opcaps_check(gcaps, OPCAP_CREATE_USER) >= 0) {
+    cl = " class=\"b0\"";
+    fprintf(out_f, "<table%s><tr>", cl);
+    fprintf(out_f, "<td%s>%s[%s]</a></td>", cl,
+            html_hyperref(hbuf, sizeof(hbuf), phr->session_id, phr->self_url,
+                          NULL, "action=%d&amp;op=%d",
+                          SSERV_CMD_HTTP_REQUEST, SSERV_OP_GROUP_CREATE_PAGE),
+            "Create");
+    fprintf(out_f, "</tr></table>\n");
+  }
+
+do_footer:
+  ss_write_html_footer(out_f);
+
+cleanup:
+  xfree(xml_text); xml_text = 0;
+  userlist_free(&users->b); users = 0;
+  html_armor_free(&ab);
+  return retval;
+}
+
+int
+super_serve_GROUP_FILTER_CHANGE_ACTION(
+        FILE *log_f,
+        FILE *out_f,
+        struct super_http_request_info *phr)
+{
+  int r, value, retval = 0;
+  opcap_t gcaps = 0;
+  long long total_count = 0;
+  int group_offset = 0;
+  int group_count = 0;
+
+  if (get_global_caps(phr, &gcaps) < 0 && opcaps_check(gcaps, OPCAP_LIST_USERS) < 0) {
+    FAIL(S_ERR_PERM_DENIED);
+  }
+
+  if (!phr->userlist_clnt) {
+    goto cleanup;
+  }
+  if ((r = userlist_clnt_get_count(phr->userlist_clnt, ULS_GET_GROUP_COUNT,
+                                   0, 0, 0, &total_count)) < 0) {
+    err("set_group_filter: get_count failed: %d", -r);
+    goto cleanup;
+  }
+  if (total_count <= 0) goto cleanup;
+  if (phr->ss->group_filter_set) {
+    group_offset = phr->ss->group_offset;
+    group_count = phr->ss->group_count;
+  }
+  if (group_count <= 0) group_count = 20;
+  if (group_count > 200) group_count = 200;
+
+  switch (phr->opcode) {
+  case SSERV_OP_GROUP_FILTER_CHANGE_ACTION:
+    if (ss_cgi_param_int(phr, "group_offset", &value) >= 0) {
+      group_offset = value;
+    }
+    if (ss_cgi_param_int(phr, "group_count", &value) >= 0) {
+      group_count = value;
+    }
+    if (group_count <= 0) group_count = 20;
+    if (group_count > 200) group_count = 200;
+    break;
+
+  case SSERV_OP_GROUP_FILTER_FIRST_PAGE_ACTION:
+    group_offset = 0;
+    break;
+  case SSERV_OP_GROUP_FILTER_PREV_PAGE_ACTION:
+    group_offset -= group_count;
+    break;
+  case SSERV_OP_GROUP_FILTER_NEXT_PAGE_ACTION:
+    group_offset += group_count;
+    break;
+  case SSERV_OP_GROUP_FILTER_LAST_PAGE_ACTION:
+    group_offset = group_count;
+    break;
+  }
+
+  if (group_offset + group_count > total_count) {
+    group_offset = total_count - group_count;
+  }
+  if (group_offset < 0) group_offset = 0;
+  phr->ss->group_filter_set = 1;
+  phr->ss->group_offset = group_offset;
+  phr->ss->group_count = group_count;
+
+cleanup:
+  ss_redirect(out_f, phr, SSERV_OP_GROUP_BROWSE_PAGE, NULL);
+  return retval;
+}
+
+int
+super_serve_op_GROUP_CREATE_PAGE(
+        FILE *log_f,
+        FILE *out_f,
+        struct super_http_request_info *phr)
+{
+  int retval = 0;
+  unsigned char buf[1024];
+  const unsigned char *cl = 0;
+  opcap_t caps = 0;
+
+  if (get_global_caps(phr, &caps) < 0 || opcaps_check(caps, OPCAP_CREATE_USER) < 0) {
+    FAIL(S_ERR_PERM_DENIED);
+  }
+
+  snprintf(buf, sizeof(buf), "serve-control: %s, create a new group",
+           phr->html_name);
+  ss_write_html_header(out_f, phr, buf, 0, NULL);
+
+  fprintf(out_f, "<h1>%s</h1>\n", buf);
+
+  print_top_navigation_links(log_f, out_f, phr, 0, 0, 0, NULL);
+
+  html_start_form(out_f, 1, phr->self_url, "");
+  html_hidden(out_f, "SID", "%016llx", phr->session_id);
+  html_hidden(out_f, "action", "%d", SSERV_CMD_HTTP_REQUEST);
+  html_hidden(out_f, "op", "%d", SSERV_OP_GROUP_CREATE_ACTION);
+
+  cl = " class=\"b0\"";
+  fprintf(out_f, "<table%s>\n", cl);
+  fprintf(out_f, "<tr><td%s><b>%s*:</b></td><td%s><input type=\"text\" size=\"80\" name=\"group_name\" /></td></tr>\n",
+          cl, "Group Name", cl);
+  fprintf(out_f, "<tr><td%s><b>%s*:</b></td><td%s><input type=\"text\" size=\"80\" name=\"description\" /></td></tr>\n",
+          cl, "Description", cl);
+  fprintf(out_f, "<tr><td%s>&nbsp;</td><td%s><input type=\"submit\" name=\"submit\" value=\"%s\" /></td></tr>\n",
+          cl, cl, "Create a group");
+  fprintf(out_f, "</table>\n");
+  fprintf(out_f, "</form>\n");
+
+  ss_write_html_footer(out_f);
+
+cleanup:
+  return retval;
+}
+
+int
+super_serve_op_GROUP_MODIFY_PAGE(
+        FILE *log_f,
+        FILE *out_f,
+        struct super_http_request_info *phr)
+{
+  int retval = 0, r;
+  unsigned char *xml_text = 0;
+  struct userlist_list *users = 0;
+  int group_id = 0;
+  struct userlist_group *g = 0;
+  unsigned char buf[1024];
+  const unsigned char *cl = 0;
+  const unsigned char *s;
+  struct html_armor_buffer ab = HTML_ARMOR_INITIALIZER;
+  opcap_t caps = 0;
+
+  if (get_global_caps(phr, &caps) < 0 || opcaps_check(caps, OPCAP_EDIT_USER) < 0) {
+    FAIL(S_ERR_PERM_DENIED);
+  }
+
+  ss_cgi_param_int_opt(phr, "group_id", &group_id, 0);
+  if (group_id <= 0) FAIL(S_ERR_INV_GROUP_ID);
+
+  if (!phr->userlist_clnt) FAIL(S_ERR_DB_ERROR);
+  r = userlist_clnt_list_all_users(phr->userlist_clnt, ULS_GET_GROUP_INFO,
+                                   group_id, &xml_text);
+  if (r < 0) FAIL(S_ERR_DB_ERROR);
+  users = userlist_parse_str(xml_text);
+  if (!users) FAIL(S_ERR_DB_ERROR);
+  if (group_id >= users->group_map_size || !(g = users->group_map[group_id]))
+    FAIL(S_ERR_INV_GROUP_ID);
+
+  snprintf(buf, sizeof(buf), "serve-control: %s, modifying group %d",
+           phr->html_name, group_id);
+  ss_write_html_header(out_f, phr, buf, 0, NULL);
+
+  fprintf(out_f, "<h1>%s</h1>\n", buf);
+
+  print_top_navigation_links(log_f, out_f, phr, 0, group_id, 0, NULL);
+
+  html_start_form(out_f, 1, phr->self_url, "");
+  html_hidden(out_f, "SID", "%016llx", phr->session_id);
+  html_hidden(out_f, "action", "%d", SSERV_CMD_HTTP_REQUEST);
+  html_hidden(out_f, "group_id", "%d", group_id);
+
+  cl = " class=\"b0\"";
+  fprintf(out_f, "<table%s>\n", cl);
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>%d</td></tr>\n",
+          cl, "Group Id", cl, group_id);
+  s = g->group_name;
+  if (!s) s = "";
+  fprintf(out_f, "<tr><td%s><b>%s*:</b></td><td%s><input type=\"text\" size=\"80\" name=\"group_name\" value=\"%s\" /></td></tr>\n",
+          cl, "Group Name", cl, ARMOR(s));
+  s = g->description;
+  if (!s) s = "";
+  fprintf(out_f, "<tr><td%s><b>%s*:</b></td><td%s><input type=\"text\" size=\"80\" name=\"description\" value=\"%s\" /></td></tr>\n",
+          cl, "Description", cl, ARMOR(s));
+  fprintf(out_f, "<tr><td%s colspan=\"2\">", cl);
+  fprintf(out_f, "<input type=\"submit\" name=\"op_%d\" value=\"%s\" />",
+          SSERV_OP_GROUP_CANCEL_ACTION, "Cancel");
+  fprintf(out_f, "&nbsp;<input type=\"submit\" name=\"op_%d\" value=\"%s\" />",
+          SSERV_OP_GROUP_DELETE_PAGE_ACTION, "Delete the group!");
+  fprintf(out_f, "&nbsp;<input type=\"submit\" name=\"op_%d\" value=\"%s\" />",
+          SSERV_OP_GROUP_MODIFY_ACTION, "Save changes");
+  fprintf(out_f, "</td></tr>\n");
+  fprintf(out_f, "</table>\n");
+  fprintf(out_f, "</form>\n");
+
+  ss_write_html_footer(out_f);
+
+cleanup:
+  html_armor_free(&ab);
+  userlist_free(&users->b); users = 0;
+  xfree(xml_text);
+  return retval;
+}
+
+int
+super_serve_op_GROUP_DELETE_PAGE(
+        FILE *log_f,
+        FILE *out_f,
+        struct super_http_request_info *phr)
+{
+  int retval = 0, r;
+  unsigned char *xml_text = 0;
+  struct userlist_list *users = 0;
+  int group_id = 0;
+  struct userlist_group *g = 0;
+  unsigned char buf[1024];
+  const unsigned char *cl = 0;
+  const unsigned char *s;
+  struct html_armor_buffer ab = HTML_ARMOR_INITIALIZER;
+  opcap_t caps = 0;
+
+  if (get_global_caps(phr, &caps) < 0 || opcaps_check(caps, OPCAP_EDIT_USER) < 0) {
+    FAIL(S_ERR_PERM_DENIED);
+  }
+
+  ss_cgi_param_int_opt(phr, "group_id", &group_id, 0);
+  if (group_id <= 0) FAIL(S_ERR_INV_GROUP_ID);
+
+  if (!phr->userlist_clnt) FAIL(S_ERR_DB_ERROR);
+  r = userlist_clnt_list_all_users(phr->userlist_clnt, ULS_GET_GROUP_INFO,
+                                   group_id, &xml_text);
+  if (r < 0) FAIL(S_ERR_DB_ERROR);
+  users = userlist_parse_str(xml_text);
+  if (!users) FAIL(S_ERR_DB_ERROR);
+  if (group_id >= users->group_map_size || !(g = users->group_map[group_id]))
+    FAIL(S_ERR_INV_GROUP_ID);
+
+  snprintf(buf, sizeof(buf), "serve-control: %s, modifying group %d",
+           phr->html_name, group_id);
+  ss_write_html_header(out_f, phr, buf, 0, NULL);
+
+  fprintf(out_f, "<h1>%s</h1>\n", buf);
+
+  print_top_navigation_links(log_f, out_f, phr, 0, group_id, 0, NULL);
+
+  html_start_form(out_f, 1, phr->self_url, "");
+  html_hidden(out_f, "SID", "%016llx", phr->session_id);
+  html_hidden(out_f, "action", "%d", SSERV_CMD_HTTP_REQUEST);
+  html_hidden(out_f, "group_id", "%d", group_id);
+
+  cl = " class=\"b0\"";
+  fprintf(out_f, "<table%s>\n", cl);
+  fprintf(out_f, "<tr><td%s><b>%s:</b></td><td%s>%d</td></tr>\n",
+          cl, "Group Id", cl, group_id);
+  s = g->group_name;
+  if (!s) s = "";
+  fprintf(out_f, "<tr><td%s><b>%s*:</b></td><td%s>%s</td></tr>\n",
+          cl, "Group Name", cl, ARMOR(s));
+  s = g->description;
+  if (!s) s = "";
+  fprintf(out_f, "<tr><td%s><b>%s*:</b></td><td%s>%s</td></tr>\n",
+          cl, "Description", cl, ARMOR(s));
+  fprintf(out_f, "<tr><td%s colspan=\"2\">", cl);
+  fprintf(out_f, "<input type=\"submit\" name=\"op_%d\" value=\"%s\" />",
+          SSERV_OP_GROUP_CANCEL_ACTION, "Cancel");
+  fprintf(out_f, "&nbsp;<input type=\"submit\" name=\"op_%d\" value=\"%s\" />",
+          SSERV_OP_GROUP_DELETE_ACTION, "Delete the group!");
+  fprintf(out_f, "&nbsp;<input type=\"submit\" name=\"op_%d\" value=\"%s\" />",
+          SSERV_OP_GROUP_MODIFY_PAGE_ACTION, "Modify the group");
+  fprintf(out_f, "</td></tr>\n");
+  fprintf(out_f, "</table>\n");
+  fprintf(out_f, "</form>\n");
+
+  ss_write_html_footer(out_f);
+
+cleanup:
+  html_armor_free(&ab);
+  userlist_free(&users->b); users = 0;
+  xfree(xml_text);
+  return retval;
+}
+
+int
+super_serve_op_GROUP_DELETE_PAGE_ACTION(
+        FILE *log_f,
+        FILE *out_f,
+        struct super_http_request_info *phr)
+{
+  int group_id = 0;
+
+  ss_cgi_param_int_opt(phr, "group_id", &group_id, 0);
+  if (group_id <= 0) group_id = 0;
+  ss_redirect_2(out_f, phr, SSERV_OP_GROUP_DELETE_PAGE, 0, group_id, 0, 0);
+  return 0;
+}
+
+int
+super_serve_op_GROUP_MODIFY_PAGE_ACTION(
+        FILE *log_f,
+        FILE *out_f,
+        struct super_http_request_info *phr)
+{
+  int group_id = 0;
+
+  ss_cgi_param_int_opt(phr, "group_id", &group_id, 0);
+  if (group_id <= 0) group_id = 0;
+  ss_redirect_2(out_f, phr, SSERV_OP_GROUP_MODIFY_PAGE, 0, group_id, 0, 0);
+  return 0;
+}
+
+int
+super_serve_op_GROUP_CREATE_ACTION(
+        FILE *log_f,
+        FILE *out_f,
+        struct super_http_request_info *phr)
+{
+  int retval = 0, r, group_id = 0;
+  opcap_t caps = 0;
+  const unsigned char *s = NULL;
+  unsigned char *group_name = NULL;
+  unsigned char *description = NULL;
+
+  if (get_global_caps(phr, &caps) < 0 || opcaps_check(caps, OPCAP_CREATE_USER) < 0) {
+    FAIL(S_ERR_PERM_DENIED);
+  }
+
+  s = 0;
+  if (ss_cgi_param(phr, "group_name", &s) <= 0 || !s) FAIL(S_ERR_INV_GROUP_NAME);
+  group_name = fix_string(s);
+  if (!group_name || !*group_name) FAIL(S_ERR_INV_GROUP_NAME);
+  if (strlen(group_name) > 1024) FAIL(S_ERR_INV_GROUP_NAME);
+  if (check_str(group_name, login_accept_chars) < 0) FAIL(S_ERR_INV_GROUP_NAME);
+
+  s = 0;
+  if (ss_cgi_param(phr, "description", &s) < 0) FAIL(S_ERR_INV_DESCRIPTION);
+  if (!s) {
+    description = xstrdup("");
+  } else {
+    description = fix_string(s);
+  }
+  if (!description) description = xstrdup("");
+  if (strlen(description) > 1024) FAIL(S_ERR_INV_DESCRIPTION);
+
+  r = userlist_clnt_create_user(phr->userlist_clnt, ULS_CREATE_GROUP, group_name, &group_id);
+  if (r < 0 || group_name < 0) FAIL(S_ERR_GROUP_CREATION_FAILED);
+  r = userlist_clnt_edit_field(phr->userlist_clnt, ULS_EDIT_GROUP_FIELD, group_id, 0,
+                               0, USERLIST_GRP_DESCRIPTION, description);
+  if (r < 0) FAIL(S_ERR_DB_ERROR);
+
+  ss_redirect_2(out_f, phr, SSERV_OP_GROUP_BROWSE_PAGE, 0, 0, 0, 0);
+
+cleanup:
+  xfree(group_name); group_name = NULL;
+  xfree(description); description = NULL;
+  return retval;
+}
+
+int
+super_serve_op_GROUP_MODIFY_ACTION(
+        FILE *log_f,
+        FILE *out_f,
+        struct super_http_request_info *phr)
+{
+  int retval = 0, r;
+  int group_id = 0;
+  unsigned char *xml_text = 0;
+  struct userlist_list *users = 0;
+  opcap_t caps = 0;
+  const struct userlist_group *g = 0;
+  const unsigned char *s;
+  unsigned char *group_name = NULL;
+  unsigned char *description = NULL;
+
+  if (get_global_caps(phr, &caps) < 0 || opcaps_check(caps, OPCAP_EDIT_USER) < 0) {
+    FAIL(S_ERR_PERM_DENIED);
+  }
+
+  ss_cgi_param_int_opt(phr, "group_id", &group_id, 0);
+  if (group_id <= 0) FAIL(S_ERR_INV_GROUP_ID);
+
+  if (!phr->userlist_clnt) FAIL(S_ERR_DB_ERROR);
+  r = userlist_clnt_list_all_users(phr->userlist_clnt, ULS_GET_GROUP_INFO,
+                                   group_id, &xml_text);
+  if (r < 0) FAIL(S_ERR_DB_ERROR);
+  users = userlist_parse_str(xml_text);
+  if (!users) FAIL(S_ERR_DB_ERROR);
+  if (group_id >= users->group_map_size || !(g = users->group_map[group_id]))
+    FAIL(S_ERR_INV_GROUP_ID);
+
+  s = 0;
+  if (ss_cgi_param(phr, "group_name", &s) <= 0 || !s) FAIL(S_ERR_INV_GROUP_NAME);
+  group_name = fix_string(s);
+  if (!group_name || !*group_name) FAIL(S_ERR_INV_GROUP_NAME);
+  if (strlen(group_name) > 1024) FAIL(S_ERR_INV_GROUP_NAME);
+  if (check_str(group_name, login_accept_chars) < 0) FAIL(S_ERR_INV_GROUP_NAME);
+
+  s = 0;
+  if (ss_cgi_param(phr, "description", &s) < 0) FAIL(S_ERR_INV_DESCRIPTION);
+  if (!s) {
+    description = xstrdup("");
+  } else {
+    description = fix_string(s);
+  }
+  if (!description) description = xstrdup("");
+  if (strlen(description) > 1024) FAIL(S_ERR_INV_DESCRIPTION);
+
+  if (!g->group_name || strcmp(g->group_name, group_name) != 0) {
+    r = userlist_clnt_edit_field(phr->userlist_clnt, ULS_EDIT_GROUP_FIELD,
+                                 group_id, 0, 0, USERLIST_GRP_GROUP_NAME, group_name);
+    if (r < 0) FAIL(S_ERR_DB_ERROR);
+  }
+
+  if (!g->description || strcmp(g->description, description) != 0) {
+    r = userlist_clnt_edit_field(phr->userlist_clnt, ULS_EDIT_GROUP_FIELD,
+                                 group_id, 0, 0, USERLIST_GRP_DESCRIPTION, description);
+    if (r < 0) FAIL(S_ERR_DB_ERROR);
+  }
+
+  ss_redirect_2(out_f, phr, SSERV_OP_GROUP_BROWSE_PAGE, 0, 0, 0, 0);
+
+cleanup:
+  userlist_free(&users->b); users = 0;
+  xfree(xml_text); xml_text = 0;
+  xfree(group_name); group_name = NULL;
+  xfree(description); description = NULL;
+  return retval;
+}
+
+int
+super_serve_op_GROUP_DELETE_ACTION(
+        FILE *log_f,
+        FILE *out_f,
+        struct super_http_request_info *phr)
+{
+  int retval = 0, r;
+  int group_id = 0;
+  unsigned char *xml_text = 0;
+  struct userlist_list *users = 0;
+  opcap_t caps = 0;
+  const struct userlist_group *g = 0;
+
+  if (get_global_caps(phr, &caps) < 0 || opcaps_check(caps, OPCAP_DELETE_USER) < 0) {
+    FAIL(S_ERR_PERM_DENIED);
+  }
+
+  ss_cgi_param_int_opt(phr, "group_id", &group_id, 0);
+  if (group_id <= 0) FAIL(S_ERR_INV_GROUP_ID);
+
+  if (!phr->userlist_clnt) FAIL(S_ERR_DB_ERROR);
+  r = userlist_clnt_list_all_users(phr->userlist_clnt, ULS_GET_GROUP_INFO,
+                                   group_id, &xml_text);
+  if (r < 0) FAIL(S_ERR_DB_ERROR);
+  users = userlist_parse_str(xml_text);
+  if (!users) FAIL(S_ERR_DB_ERROR);
+  if (group_id >= users->group_map_size || !(g = users->group_map[group_id]))
+    FAIL(S_ERR_INV_GROUP_ID);
+
+  r = userlist_clnt_delete_info(phr->userlist_clnt, ULS_DELETE_GROUP, group_id, 0, 0);
+  if (r < 0) FAIL(S_ERR_DB_ERROR);
+
+  ss_redirect_2(out_f, phr, SSERV_OP_GROUP_BROWSE_PAGE, 0, 0, 0, 0);
+
+cleanup:
+  userlist_free(&users->b); users = 0;
+  xfree(xml_text); xml_text = 0;
+  return retval;
+}
diff -ruNp ejudge-2.3.18/super_html_6.h ejudge/super_html_6.h
--- ejudge-2.3.18/super_html_6.h	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/super_html_6.h	2011-07-11 09:02:04.146280698 +0400
@@ -0,0 +1,163 @@
+/* -*- c -*- */
+/* $Id: super_html_6.h 6339 2011-05-22 07:43:24Z cher $ */
+
+#ifndef __SUPER_HTML_6_H__
+#define __SUPER_HTML_6_H__
+
+/* Copyright (C) 2011 Alexander Chernov <cher@ejudge.ru> */
+
+/*
+ * 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.
+ */
+
+#include "ej_types.h"
+
+struct ss_op_param_USER_CREATE_ONE_ACTION
+{
+  ej_int_opt_0_t   contest_id;
+  ej_int_opt_0_t   group_id;
+  ej_textbox_t     other_login;
+  ej_textbox_opt_t other_email;
+  ej_checkbox_t    send_email;
+  ej_checkbox_t    confirm_email;
+  ej_textbox_t     reg_password1;
+  ej_textbox_t     reg_password2;
+  ej_checkbox_t    reg_sha1;
+  ej_checkbox_t    field_1;          // USERLIST_NN_IS_PRIVILEGED,
+  ej_checkbox_t    field_2;          // USERLIST_NN_IS_INVISIBLE,
+  ej_checkbox_t    field_3;          // USERLIST_NN_IS_BANNED,
+  ej_checkbox_t    field_4;          // USERLIST_NN_IS_LOCKED,
+  ej_checkbox_t    field_5;          // USERLIST_NN_SHOW_LOGIN,
+  ej_checkbox_t    field_6;          // USERLIST_NN_SHOW_EMAIL,
+  ej_checkbox_t    field_7;          // USERLIST_NN_READ_ONLY,
+  ej_checkbox_t    field_8;          // USERLIST_NN_NEVER_CLEAN,
+  ej_checkbox_t    field_9;          // USERLIST_NN_SIMPLE_REGISTRATION,
+  ej_checkbox_t    reg_cnts_create;
+  ej_int_opt_0_t   other_contest_id_1;
+  ej_int_opt_1_t   cnts_status;
+  ej_checkbox_t    is_invisible;
+  ej_checkbox_t    is_banned;
+  ej_checkbox_t    is_locked;
+  ej_checkbox_t    is_incomplete;
+  ej_checkbox_t    is_disqualified;
+  ej_checkbox_t    cnts_use_reg_passwd;
+  ej_checkbox_t    cnts_null_passwd;
+  ej_textbox_opt_t cnts_password1;
+  ej_textbox_opt_t cnts_password2;
+  ej_checkbox_t    cnts_sha1;
+  ej_textbox_opt_t cnts_name;
+  ej_checkbox_t    group_create;
+  ej_int_opt_0_t   other_group_id;
+};
+
+struct ss_op_param_USER_CREATE_MANY_ACTION
+{
+  ej_int_opt_0_t   contest_id;
+  ej_int_opt_0_t   group_id;
+  ej_int_opt_m1_t  first_serial;
+  ej_int_opt_m1_t  last_serial;
+  ej_textbox_t     login_template;
+  ej_checkbox_t    reg_random;
+  ej_textbox_opt_t reg_password_template;
+  ej_checkbox_t    reg_sha1;
+  ej_checkbox_t    field_1;          // USERLIST_NN_IS_PRIVILEGED,
+  ej_checkbox_t    field_2;          // USERLIST_NN_IS_INVISIBLE,
+  ej_checkbox_t    field_3;          // USERLIST_NN_IS_BANNED,
+  ej_checkbox_t    field_4;          // USERLIST_NN_IS_LOCKED,
+  ej_checkbox_t    field_5;          // USERLIST_NN_SHOW_LOGIN,
+  ej_checkbox_t    field_6;          // USERLIST_NN_SHOW_EMAIL,
+  ej_checkbox_t    field_7;          // USERLIST_NN_READ_ONLY,
+  ej_checkbox_t    field_8;          // USERLIST_NN_NEVER_CLEAN,
+  ej_checkbox_t    field_9;          // USERLIST_NN_SIMPLE_REGISTRATION,
+  ej_checkbox_t    reg_cnts_create;
+  ej_int_opt_0_t   other_contest_id_1;
+  ej_int_opt_1_t   cnts_status;
+  ej_checkbox_t    is_invisible;
+  ej_checkbox_t    is_banned;
+  ej_checkbox_t    is_locked;
+  ej_checkbox_t    is_incomplete;
+  ej_checkbox_t    is_disqualified;
+  ej_checkbox_t    cnts_use_reg_passwd;
+  ej_checkbox_t    cnts_null_passwd;
+  ej_checkbox_t    cnts_random_passwd;
+  ej_textbox_opt_t cnts_password_template;
+  ej_checkbox_t    cnts_sha1;
+  ej_textbox_opt_t cnts_name_template;
+  ej_checkbox_t    group_create;
+  ej_int_opt_0_t   other_group_id;
+};
+
+struct ss_op_param_USER_CREATE_FROM_CSV_ACTION
+{
+  ej_int_opt_0_t   contest_id;
+  ej_int_opt_0_t   group_id;
+  ej_checkbox_t    send_email;
+  ej_checkbox_t    confirm_email;
+  ej_checkbox_t    reg_random;
+  ej_checkbox_t    reg_sha1;
+  ej_checkbox_t    field_1;          // USERLIST_NN_IS_PRIVILEGED,
+  ej_checkbox_t    field_2;          // USERLIST_NN_IS_INVISIBLE,
+  ej_checkbox_t    field_3;          // USERLIST_NN_IS_BANNED,
+  ej_checkbox_t    field_4;          // USERLIST_NN_IS_LOCKED,
+  ej_checkbox_t    field_5;          // USERLIST_NN_SHOW_LOGIN,
+  ej_checkbox_t    field_6;          // USERLIST_NN_SHOW_EMAIL,
+  ej_checkbox_t    field_7;          // USERLIST_NN_READ_ONLY,
+  ej_checkbox_t    field_8;          // USERLIST_NN_NEVER_CLEAN,
+  ej_checkbox_t    field_9;          // USERLIST_NN_SIMPLE_REGISTRATION,
+  ej_checkbox_t    reg_cnts_create;
+  ej_int_opt_0_t   other_contest_id_1;
+  ej_int_opt_1_t   cnts_status;
+  ej_checkbox_t    is_invisible;
+  ej_checkbox_t    is_banned;
+  ej_checkbox_t    is_locked;
+  ej_checkbox_t    is_incomplete;
+  ej_checkbox_t    is_disqualified;
+  ej_checkbox_t    cnts_use_reg_passwd;
+  ej_checkbox_t    cnts_null_passwd;
+  ej_checkbox_t    cnts_random_passwd;
+  ej_checkbox_t    cnts_sha1;
+  ej_textbox_opt_t cnts_name_template;
+  ej_checkbox_t    group_create;
+  ej_int_opt_0_t   other_group_id;
+  ej_textbox_opt_t separator;
+  ej_textbox_opt_t charset;
+};
+
+struct ss_op_param_USER_CREATE_REG_ACTION
+{
+  ej_int_opt_0_t   other_user_id;
+  ej_int_opt_0_t   contest_id;
+  ej_int_opt_0_t   group_id;
+  ej_int_opt_0_t   other_contest_id_1;
+  ej_int_opt_1_t   status;
+  ej_checkbox_t    is_invisible;
+  ej_checkbox_t    is_banned;
+  ej_checkbox_t    is_locked;
+  ej_checkbox_t    is_incomplete;
+  ej_checkbox_t    is_disqualified;
+};
+
+struct ss_op_param_USER_EDIT_REG_ACTION
+{
+  ej_int_opt_0_t   other_user_id;
+  ej_int_opt_0_t   other_contest_id;
+  ej_int_opt_0_t   contest_id;
+  ej_int_opt_0_t   group_id;
+  ej_int_opt_0_t   next_op;
+  ej_int_opt_1_t   status;
+  ej_checkbox_t    is_invisible;
+  ej_checkbox_t    is_banned;
+  ej_checkbox_t    is_locked;
+  ej_checkbox_t    is_incomplete;
+  ej_checkbox_t    is_disqualified;
+};
+
+#endif /* __SUPER_HTML_6_H__ */
diff -ruNp ejudge-2.3.18/super_html_6_meta.c ejudge/super_html_6_meta.c
--- ejudge-2.3.18/super_html_6_meta.c	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/super_html_6_meta.c	2011-07-11 09:02:04.358276800 +0400
@@ -0,0 +1,403 @@
+// This is an auto-generated file, do not edit
+// Generated 2011/05/22 11:43:29
+
+#include "super_html_6_meta.h"
+#include "super_html_6.h"
+#include "meta_generic.h"
+
+#include "reuse_xalloc.h"
+
+#include "reuse_logger.h"
+#include <string.h>
+#include <stdlib.h>
+
+static struct meta_info_item meta_info_ss_op_param_USER_CREATE_ONE_ACTION_data[] =
+{
+  [META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_contest_id] = { META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_contest_id, '0', XSIZE(struct ss_op_param_USER_CREATE_ONE_ACTION, contest_id), "contest_id", XOFFSET(struct ss_op_param_USER_CREATE_ONE_ACTION, contest_id) },
+  [META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_group_id] = { META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_group_id, '0', XSIZE(struct ss_op_param_USER_CREATE_ONE_ACTION, group_id), "group_id", XOFFSET(struct ss_op_param_USER_CREATE_ONE_ACTION, group_id) },
+  [META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_other_login] = { META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_other_login, '1', XSIZE(struct ss_op_param_USER_CREATE_ONE_ACTION, other_login), "other_login", XOFFSET(struct ss_op_param_USER_CREATE_ONE_ACTION, other_login) },
+  [META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_other_email] = { META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_other_email, '2', XSIZE(struct ss_op_param_USER_CREATE_ONE_ACTION, other_email), "other_email", XOFFSET(struct ss_op_param_USER_CREATE_ONE_ACTION, other_email) },
+  [META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_send_email] = { META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_send_email, '3', XSIZE(struct ss_op_param_USER_CREATE_ONE_ACTION, send_email), "send_email", XOFFSET(struct ss_op_param_USER_CREATE_ONE_ACTION, send_email) },
+  [META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_confirm_email] = { META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_confirm_email, '3', XSIZE(struct ss_op_param_USER_CREATE_ONE_ACTION, confirm_email), "confirm_email", XOFFSET(struct ss_op_param_USER_CREATE_ONE_ACTION, confirm_email) },
+  [META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_reg_password1] = { META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_reg_password1, '1', XSIZE(struct ss_op_param_USER_CREATE_ONE_ACTION, reg_password1), "reg_password1", XOFFSET(struct ss_op_param_USER_CREATE_ONE_ACTION, reg_password1) },
+  [META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_reg_password2] = { META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_reg_password2, '1', XSIZE(struct ss_op_param_USER_CREATE_ONE_ACTION, reg_password2), "reg_password2", XOFFSET(struct ss_op_param_USER_CREATE_ONE_ACTION, reg_password2) },
+  [META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_reg_sha1] = { META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_reg_sha1, '3', XSIZE(struct ss_op_param_USER_CREATE_ONE_ACTION, reg_sha1), "reg_sha1", XOFFSET(struct ss_op_param_USER_CREATE_ONE_ACTION, reg_sha1) },
+  [META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_field_1] = { META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_field_1, '3', XSIZE(struct ss_op_param_USER_CREATE_ONE_ACTION, field_1), "field_1", XOFFSET(struct ss_op_param_USER_CREATE_ONE_ACTION, field_1) },
+  [META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_field_2] = { META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_field_2, '3', XSIZE(struct ss_op_param_USER_CREATE_ONE_ACTION, field_2), "field_2", XOFFSET(struct ss_op_param_USER_CREATE_ONE_ACTION, field_2) },
+  [META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_field_3] = { META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_field_3, '3', XSIZE(struct ss_op_param_USER_CREATE_ONE_ACTION, field_3), "field_3", XOFFSET(struct ss_op_param_USER_CREATE_ONE_ACTION, field_3) },
+  [META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_field_4] = { META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_field_4, '3', XSIZE(struct ss_op_param_USER_CREATE_ONE_ACTION, field_4), "field_4", XOFFSET(struct ss_op_param_USER_CREATE_ONE_ACTION, field_4) },
+  [META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_field_5] = { META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_field_5, '3', XSIZE(struct ss_op_param_USER_CREATE_ONE_ACTION, field_5), "field_5", XOFFSET(struct ss_op_param_USER_CREATE_ONE_ACTION, field_5) },
+  [META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_field_6] = { META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_field_6, '3', XSIZE(struct ss_op_param_USER_CREATE_ONE_ACTION, field_6), "field_6", XOFFSET(struct ss_op_param_USER_CREATE_ONE_ACTION, field_6) },
+  [META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_field_7] = { META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_field_7, '3', XSIZE(struct ss_op_param_USER_CREATE_ONE_ACTION, field_7), "field_7", XOFFSET(struct ss_op_param_USER_CREATE_ONE_ACTION, field_7) },
+  [META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_field_8] = { META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_field_8, '3', XSIZE(struct ss_op_param_USER_CREATE_ONE_ACTION, field_8), "field_8", XOFFSET(struct ss_op_param_USER_CREATE_ONE_ACTION, field_8) },
+  [META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_field_9] = { META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_field_9, '3', XSIZE(struct ss_op_param_USER_CREATE_ONE_ACTION, field_9), "field_9", XOFFSET(struct ss_op_param_USER_CREATE_ONE_ACTION, field_9) },
+  [META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_reg_cnts_create] = { META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_reg_cnts_create, '3', XSIZE(struct ss_op_param_USER_CREATE_ONE_ACTION, reg_cnts_create), "reg_cnts_create", XOFFSET(struct ss_op_param_USER_CREATE_ONE_ACTION, reg_cnts_create) },
+  [META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_other_contest_id_1] = { META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_other_contest_id_1, '0', XSIZE(struct ss_op_param_USER_CREATE_ONE_ACTION, other_contest_id_1), "other_contest_id_1", XOFFSET(struct ss_op_param_USER_CREATE_ONE_ACTION, other_contest_id_1) },
+  [META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_cnts_status] = { META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_cnts_status, '4', XSIZE(struct ss_op_param_USER_CREATE_ONE_ACTION, cnts_status), "cnts_status", XOFFSET(struct ss_op_param_USER_CREATE_ONE_ACTION, cnts_status) },
+  [META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_is_invisible] = { META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_is_invisible, '3', XSIZE(struct ss_op_param_USER_CREATE_ONE_ACTION, is_invisible), "is_invisible", XOFFSET(struct ss_op_param_USER_CREATE_ONE_ACTION, is_invisible) },
+  [META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_is_banned] = { META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_is_banned, '3', XSIZE(struct ss_op_param_USER_CREATE_ONE_ACTION, is_banned), "is_banned", XOFFSET(struct ss_op_param_USER_CREATE_ONE_ACTION, is_banned) },
+  [META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_is_locked] = { META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_is_locked, '3', XSIZE(struct ss_op_param_USER_CREATE_ONE_ACTION, is_locked), "is_locked", XOFFSET(struct ss_op_param_USER_CREATE_ONE_ACTION, is_locked) },
+  [META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_is_incomplete] = { META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_is_incomplete, '3', XSIZE(struct ss_op_param_USER_CREATE_ONE_ACTION, is_incomplete), "is_incomplete", XOFFSET(struct ss_op_param_USER_CREATE_ONE_ACTION, is_incomplete) },
+  [META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_is_disqualified] = { META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_is_disqualified, '3', XSIZE(struct ss_op_param_USER_CREATE_ONE_ACTION, is_disqualified), "is_disqualified", XOFFSET(struct ss_op_param_USER_CREATE_ONE_ACTION, is_disqualified) },
+  [META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_cnts_use_reg_passwd] = { META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_cnts_use_reg_passwd, '3', XSIZE(struct ss_op_param_USER_CREATE_ONE_ACTION, cnts_use_reg_passwd), "cnts_use_reg_passwd", XOFFSET(struct ss_op_param_USER_CREATE_ONE_ACTION, cnts_use_reg_passwd) },
+  [META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_cnts_null_passwd] = { META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_cnts_null_passwd, '3', XSIZE(struct ss_op_param_USER_CREATE_ONE_ACTION, cnts_null_passwd), "cnts_null_passwd", XOFFSET(struct ss_op_param_USER_CREATE_ONE_ACTION, cnts_null_passwd) },
+  [META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_cnts_password1] = { META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_cnts_password1, '2', XSIZE(struct ss_op_param_USER_CREATE_ONE_ACTION, cnts_password1), "cnts_password1", XOFFSET(struct ss_op_param_USER_CREATE_ONE_ACTION, cnts_password1) },
+  [META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_cnts_password2] = { META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_cnts_password2, '2', XSIZE(struct ss_op_param_USER_CREATE_ONE_ACTION, cnts_password2), "cnts_password2", XOFFSET(struct ss_op_param_USER_CREATE_ONE_ACTION, cnts_password2) },
+  [META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_cnts_sha1] = { META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_cnts_sha1, '3', XSIZE(struct ss_op_param_USER_CREATE_ONE_ACTION, cnts_sha1), "cnts_sha1", XOFFSET(struct ss_op_param_USER_CREATE_ONE_ACTION, cnts_sha1) },
+  [META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_cnts_name] = { META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_cnts_name, '2', XSIZE(struct ss_op_param_USER_CREATE_ONE_ACTION, cnts_name), "cnts_name", XOFFSET(struct ss_op_param_USER_CREATE_ONE_ACTION, cnts_name) },
+  [META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_group_create] = { META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_group_create, '3', XSIZE(struct ss_op_param_USER_CREATE_ONE_ACTION, group_create), "group_create", XOFFSET(struct ss_op_param_USER_CREATE_ONE_ACTION, group_create) },
+  [META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_other_group_id] = { META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_other_group_id, '0', XSIZE(struct ss_op_param_USER_CREATE_ONE_ACTION, other_group_id), "other_group_id", XOFFSET(struct ss_op_param_USER_CREATE_ONE_ACTION, other_group_id) },
+};
+
+int meta_ss_op_param_USER_CREATE_ONE_ACTION_get_type(int tag)
+{
+  ASSERT(tag > 0 && tag < META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_LAST_FIELD);
+  return meta_info_ss_op_param_USER_CREATE_ONE_ACTION_data[tag].type;
+}
+
+size_t meta_ss_op_param_USER_CREATE_ONE_ACTION_get_size(int tag)
+{
+  ASSERT(tag > 0 && tag < META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_LAST_FIELD);
+  return meta_info_ss_op_param_USER_CREATE_ONE_ACTION_data[tag].size;
+}
+
+const char *meta_ss_op_param_USER_CREATE_ONE_ACTION_get_name(int tag)
+{
+  ASSERT(tag > 0 && tag < META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_LAST_FIELD);
+  return meta_info_ss_op_param_USER_CREATE_ONE_ACTION_data[tag].name;
+}
+
+const void *meta_ss_op_param_USER_CREATE_ONE_ACTION_get_ptr(const struct ss_op_param_USER_CREATE_ONE_ACTION *ptr, int tag)
+{
+  ASSERT(tag > 0 && tag < META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_LAST_FIELD);
+  return XPDEREF(void, ptr, meta_info_ss_op_param_USER_CREATE_ONE_ACTION_data[tag].offset);
+}
+
+void *meta_ss_op_param_USER_CREATE_ONE_ACTION_get_ptr_nc(struct ss_op_param_USER_CREATE_ONE_ACTION *ptr, int tag)
+{
+  ASSERT(tag > 0 && tag < META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_LAST_FIELD);
+  return XPDEREF(void, ptr, meta_info_ss_op_param_USER_CREATE_ONE_ACTION_data[tag].offset);
+}
+
+int meta_ss_op_param_USER_CREATE_ONE_ACTION_lookup_field(const char *name)
+{
+  static struct meta_automaton *atm = 0;
+  ASSERT(name);
+  if (!atm) atm = meta_build_automaton(meta_info_ss_op_param_USER_CREATE_ONE_ACTION_data, META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_LAST_FIELD);
+  return meta_lookup_string(atm, name);
+}
+
+const struct meta_methods meta_ss_op_param_USER_CREATE_ONE_ACTION_methods =
+{
+  META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_LAST_FIELD,
+  sizeof(struct ss_op_param_USER_CREATE_ONE_ACTION),
+  meta_ss_op_param_USER_CREATE_ONE_ACTION_get_type,
+  meta_ss_op_param_USER_CREATE_ONE_ACTION_get_size,
+  meta_ss_op_param_USER_CREATE_ONE_ACTION_get_name,
+  (const void *(*)(const void *ptr, int tag))meta_ss_op_param_USER_CREATE_ONE_ACTION_get_ptr,
+  (void *(*)(void *ptr, int tag))meta_ss_op_param_USER_CREATE_ONE_ACTION_get_ptr_nc,
+  meta_ss_op_param_USER_CREATE_ONE_ACTION_lookup_field,
+};
+
+static struct meta_info_item meta_info_ss_op_param_USER_CREATE_MANY_ACTION_data[] =
+{
+  [META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_contest_id] = { META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_contest_id, '0', XSIZE(struct ss_op_param_USER_CREATE_MANY_ACTION, contest_id), "contest_id", XOFFSET(struct ss_op_param_USER_CREATE_MANY_ACTION, contest_id) },
+  [META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_group_id] = { META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_group_id, '0', XSIZE(struct ss_op_param_USER_CREATE_MANY_ACTION, group_id), "group_id", XOFFSET(struct ss_op_param_USER_CREATE_MANY_ACTION, group_id) },
+  [META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_first_serial] = { META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_first_serial, '5', XSIZE(struct ss_op_param_USER_CREATE_MANY_ACTION, first_serial), "first_serial", XOFFSET(struct ss_op_param_USER_CREATE_MANY_ACTION, first_serial) },
+  [META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_last_serial] = { META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_last_serial, '5', XSIZE(struct ss_op_param_USER_CREATE_MANY_ACTION, last_serial), "last_serial", XOFFSET(struct ss_op_param_USER_CREATE_MANY_ACTION, last_serial) },
+  [META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_login_template] = { META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_login_template, '1', XSIZE(struct ss_op_param_USER_CREATE_MANY_ACTION, login_template), "login_template", XOFFSET(struct ss_op_param_USER_CREATE_MANY_ACTION, login_template) },
+  [META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_reg_random] = { META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_reg_random, '3', XSIZE(struct ss_op_param_USER_CREATE_MANY_ACTION, reg_random), "reg_random", XOFFSET(struct ss_op_param_USER_CREATE_MANY_ACTION, reg_random) },
+  [META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_reg_password_template] = { META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_reg_password_template, '2', XSIZE(struct ss_op_param_USER_CREATE_MANY_ACTION, reg_password_template), "reg_password_template", XOFFSET(struct ss_op_param_USER_CREATE_MANY_ACTION, reg_password_template) },
+  [META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_reg_sha1] = { META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_reg_sha1, '3', XSIZE(struct ss_op_param_USER_CREATE_MANY_ACTION, reg_sha1), "reg_sha1", XOFFSET(struct ss_op_param_USER_CREATE_MANY_ACTION, reg_sha1) },
+  [META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_field_1] = { META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_field_1, '3', XSIZE(struct ss_op_param_USER_CREATE_MANY_ACTION, field_1), "field_1", XOFFSET(struct ss_op_param_USER_CREATE_MANY_ACTION, field_1) },
+  [META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_field_2] = { META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_field_2, '3', XSIZE(struct ss_op_param_USER_CREATE_MANY_ACTION, field_2), "field_2", XOFFSET(struct ss_op_param_USER_CREATE_MANY_ACTION, field_2) },
+  [META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_field_3] = { META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_field_3, '3', XSIZE(struct ss_op_param_USER_CREATE_MANY_ACTION, field_3), "field_3", XOFFSET(struct ss_op_param_USER_CREATE_MANY_ACTION, field_3) },
+  [META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_field_4] = { META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_field_4, '3', XSIZE(struct ss_op_param_USER_CREATE_MANY_ACTION, field_4), "field_4", XOFFSET(struct ss_op_param_USER_CREATE_MANY_ACTION, field_4) },
+  [META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_field_5] = { META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_field_5, '3', XSIZE(struct ss_op_param_USER_CREATE_MANY_ACTION, field_5), "field_5", XOFFSET(struct ss_op_param_USER_CREATE_MANY_ACTION, field_5) },
+  [META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_field_6] = { META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_field_6, '3', XSIZE(struct ss_op_param_USER_CREATE_MANY_ACTION, field_6), "field_6", XOFFSET(struct ss_op_param_USER_CREATE_MANY_ACTION, field_6) },
+  [META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_field_7] = { META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_field_7, '3', XSIZE(struct ss_op_param_USER_CREATE_MANY_ACTION, field_7), "field_7", XOFFSET(struct ss_op_param_USER_CREATE_MANY_ACTION, field_7) },
+  [META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_field_8] = { META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_field_8, '3', XSIZE(struct ss_op_param_USER_CREATE_MANY_ACTION, field_8), "field_8", XOFFSET(struct ss_op_param_USER_CREATE_MANY_ACTION, field_8) },
+  [META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_field_9] = { META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_field_9, '3', XSIZE(struct ss_op_param_USER_CREATE_MANY_ACTION, field_9), "field_9", XOFFSET(struct ss_op_param_USER_CREATE_MANY_ACTION, field_9) },
+  [META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_reg_cnts_create] = { META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_reg_cnts_create, '3', XSIZE(struct ss_op_param_USER_CREATE_MANY_ACTION, reg_cnts_create), "reg_cnts_create", XOFFSET(struct ss_op_param_USER_CREATE_MANY_ACTION, reg_cnts_create) },
+  [META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_other_contest_id_1] = { META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_other_contest_id_1, '0', XSIZE(struct ss_op_param_USER_CREATE_MANY_ACTION, other_contest_id_1), "other_contest_id_1", XOFFSET(struct ss_op_param_USER_CREATE_MANY_ACTION, other_contest_id_1) },
+  [META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_cnts_status] = { META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_cnts_status, '4', XSIZE(struct ss_op_param_USER_CREATE_MANY_ACTION, cnts_status), "cnts_status", XOFFSET(struct ss_op_param_USER_CREATE_MANY_ACTION, cnts_status) },
+  [META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_is_invisible] = { META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_is_invisible, '3', XSIZE(struct ss_op_param_USER_CREATE_MANY_ACTION, is_invisible), "is_invisible", XOFFSET(struct ss_op_param_USER_CREATE_MANY_ACTION, is_invisible) },
+  [META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_is_banned] = { META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_is_banned, '3', XSIZE(struct ss_op_param_USER_CREATE_MANY_ACTION, is_banned), "is_banned", XOFFSET(struct ss_op_param_USER_CREATE_MANY_ACTION, is_banned) },
+  [META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_is_locked] = { META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_is_locked, '3', XSIZE(struct ss_op_param_USER_CREATE_MANY_ACTION, is_locked), "is_locked", XOFFSET(struct ss_op_param_USER_CREATE_MANY_ACTION, is_locked) },
+  [META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_is_incomplete] = { META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_is_incomplete, '3', XSIZE(struct ss_op_param_USER_CREATE_MANY_ACTION, is_incomplete), "is_incomplete", XOFFSET(struct ss_op_param_USER_CREATE_MANY_ACTION, is_incomplete) },
+  [META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_is_disqualified] = { META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_is_disqualified, '3', XSIZE(struct ss_op_param_USER_CREATE_MANY_ACTION, is_disqualified), "is_disqualified", XOFFSET(struct ss_op_param_USER_CREATE_MANY_ACTION, is_disqualified) },
+  [META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_cnts_use_reg_passwd] = { META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_cnts_use_reg_passwd, '3', XSIZE(struct ss_op_param_USER_CREATE_MANY_ACTION, cnts_use_reg_passwd), "cnts_use_reg_passwd", XOFFSET(struct ss_op_param_USER_CREATE_MANY_ACTION, cnts_use_reg_passwd) },
+  [META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_cnts_null_passwd] = { META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_cnts_null_passwd, '3', XSIZE(struct ss_op_param_USER_CREATE_MANY_ACTION, cnts_null_passwd), "cnts_null_passwd", XOFFSET(struct ss_op_param_USER_CREATE_MANY_ACTION, cnts_null_passwd) },
+  [META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_cnts_random_passwd] = { META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_cnts_random_passwd, '3', XSIZE(struct ss_op_param_USER_CREATE_MANY_ACTION, cnts_random_passwd), "cnts_random_passwd", XOFFSET(struct ss_op_param_USER_CREATE_MANY_ACTION, cnts_random_passwd) },
+  [META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_cnts_password_template] = { META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_cnts_password_template, '2', XSIZE(struct ss_op_param_USER_CREATE_MANY_ACTION, cnts_password_template), "cnts_password_template", XOFFSET(struct ss_op_param_USER_CREATE_MANY_ACTION, cnts_password_template) },
+  [META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_cnts_sha1] = { META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_cnts_sha1, '3', XSIZE(struct ss_op_param_USER_CREATE_MANY_ACTION, cnts_sha1), "cnts_sha1", XOFFSET(struct ss_op_param_USER_CREATE_MANY_ACTION, cnts_sha1) },
+  [META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_cnts_name_template] = { META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_cnts_name_template, '2', XSIZE(struct ss_op_param_USER_CREATE_MANY_ACTION, cnts_name_template), "cnts_name_template", XOFFSET(struct ss_op_param_USER_CREATE_MANY_ACTION, cnts_name_template) },
+  [META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_group_create] = { META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_group_create, '3', XSIZE(struct ss_op_param_USER_CREATE_MANY_ACTION, group_create), "group_create", XOFFSET(struct ss_op_param_USER_CREATE_MANY_ACTION, group_create) },
+  [META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_other_group_id] = { META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_other_group_id, '0', XSIZE(struct ss_op_param_USER_CREATE_MANY_ACTION, other_group_id), "other_group_id", XOFFSET(struct ss_op_param_USER_CREATE_MANY_ACTION, other_group_id) },
+};
+
+int meta_ss_op_param_USER_CREATE_MANY_ACTION_get_type(int tag)
+{
+  ASSERT(tag > 0 && tag < META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_LAST_FIELD);
+  return meta_info_ss_op_param_USER_CREATE_MANY_ACTION_data[tag].type;
+}
+
+size_t meta_ss_op_param_USER_CREATE_MANY_ACTION_get_size(int tag)
+{
+  ASSERT(tag > 0 && tag < META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_LAST_FIELD);
+  return meta_info_ss_op_param_USER_CREATE_MANY_ACTION_data[tag].size;
+}
+
+const char *meta_ss_op_param_USER_CREATE_MANY_ACTION_get_name(int tag)
+{
+  ASSERT(tag > 0 && tag < META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_LAST_FIELD);
+  return meta_info_ss_op_param_USER_CREATE_MANY_ACTION_data[tag].name;
+}
+
+const void *meta_ss_op_param_USER_CREATE_MANY_ACTION_get_ptr(const struct ss_op_param_USER_CREATE_MANY_ACTION *ptr, int tag)
+{
+  ASSERT(tag > 0 && tag < META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_LAST_FIELD);
+  return XPDEREF(void, ptr, meta_info_ss_op_param_USER_CREATE_MANY_ACTION_data[tag].offset);
+}
+
+void *meta_ss_op_param_USER_CREATE_MANY_ACTION_get_ptr_nc(struct ss_op_param_USER_CREATE_MANY_ACTION *ptr, int tag)
+{
+  ASSERT(tag > 0 && tag < META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_LAST_FIELD);
+  return XPDEREF(void, ptr, meta_info_ss_op_param_USER_CREATE_MANY_ACTION_data[tag].offset);
+}
+
+int meta_ss_op_param_USER_CREATE_MANY_ACTION_lookup_field(const char *name)
+{
+  static struct meta_automaton *atm = 0;
+  ASSERT(name);
+  if (!atm) atm = meta_build_automaton(meta_info_ss_op_param_USER_CREATE_MANY_ACTION_data, META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_LAST_FIELD);
+  return meta_lookup_string(atm, name);
+}
+
+const struct meta_methods meta_ss_op_param_USER_CREATE_MANY_ACTION_methods =
+{
+  META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_LAST_FIELD,
+  sizeof(struct ss_op_param_USER_CREATE_MANY_ACTION),
+  meta_ss_op_param_USER_CREATE_MANY_ACTION_get_type,
+  meta_ss_op_param_USER_CREATE_MANY_ACTION_get_size,
+  meta_ss_op_param_USER_CREATE_MANY_ACTION_get_name,
+  (const void *(*)(const void *ptr, int tag))meta_ss_op_param_USER_CREATE_MANY_ACTION_get_ptr,
+  (void *(*)(void *ptr, int tag))meta_ss_op_param_USER_CREATE_MANY_ACTION_get_ptr_nc,
+  meta_ss_op_param_USER_CREATE_MANY_ACTION_lookup_field,
+};
+
+static struct meta_info_item meta_info_ss_op_param_USER_CREATE_FROM_CSV_ACTION_data[] =
+{
+  [META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_contest_id] = { META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_contest_id, '0', XSIZE(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, contest_id), "contest_id", XOFFSET(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, contest_id) },
+  [META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_group_id] = { META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_group_id, '0', XSIZE(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, group_id), "group_id", XOFFSET(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, group_id) },
+  [META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_send_email] = { META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_send_email, '3', XSIZE(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, send_email), "send_email", XOFFSET(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, send_email) },
+  [META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_confirm_email] = { META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_confirm_email, '3', XSIZE(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, confirm_email), "confirm_email", XOFFSET(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, confirm_email) },
+  [META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_reg_random] = { META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_reg_random, '3', XSIZE(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, reg_random), "reg_random", XOFFSET(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, reg_random) },
+  [META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_reg_sha1] = { META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_reg_sha1, '3', XSIZE(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, reg_sha1), "reg_sha1", XOFFSET(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, reg_sha1) },
+  [META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_field_1] = { META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_field_1, '3', XSIZE(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, field_1), "field_1", XOFFSET(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, field_1) },
+  [META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_field_2] = { META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_field_2, '3', XSIZE(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, field_2), "field_2", XOFFSET(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, field_2) },
+  [META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_field_3] = { META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_field_3, '3', XSIZE(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, field_3), "field_3", XOFFSET(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, field_3) },
+  [META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_field_4] = { META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_field_4, '3', XSIZE(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, field_4), "field_4", XOFFSET(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, field_4) },
+  [META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_field_5] = { META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_field_5, '3', XSIZE(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, field_5), "field_5", XOFFSET(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, field_5) },
+  [META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_field_6] = { META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_field_6, '3', XSIZE(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, field_6), "field_6", XOFFSET(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, field_6) },
+  [META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_field_7] = { META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_field_7, '3', XSIZE(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, field_7), "field_7", XOFFSET(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, field_7) },
+  [META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_field_8] = { META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_field_8, '3', XSIZE(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, field_8), "field_8", XOFFSET(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, field_8) },
+  [META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_field_9] = { META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_field_9, '3', XSIZE(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, field_9), "field_9", XOFFSET(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, field_9) },
+  [META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_reg_cnts_create] = { META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_reg_cnts_create, '3', XSIZE(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, reg_cnts_create), "reg_cnts_create", XOFFSET(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, reg_cnts_create) },
+  [META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_other_contest_id_1] = { META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_other_contest_id_1, '0', XSIZE(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, other_contest_id_1), "other_contest_id_1", XOFFSET(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, other_contest_id_1) },
+  [META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_cnts_status] = { META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_cnts_status, '4', XSIZE(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, cnts_status), "cnts_status", XOFFSET(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, cnts_status) },
+  [META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_is_invisible] = { META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_is_invisible, '3', XSIZE(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, is_invisible), "is_invisible", XOFFSET(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, is_invisible) },
+  [META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_is_banned] = { META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_is_banned, '3', XSIZE(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, is_banned), "is_banned", XOFFSET(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, is_banned) },
+  [META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_is_locked] = { META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_is_locked, '3', XSIZE(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, is_locked), "is_locked", XOFFSET(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, is_locked) },
+  [META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_is_incomplete] = { META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_is_incomplete, '3', XSIZE(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, is_incomplete), "is_incomplete", XOFFSET(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, is_incomplete) },
+  [META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_is_disqualified] = { META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_is_disqualified, '3', XSIZE(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, is_disqualified), "is_disqualified", XOFFSET(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, is_disqualified) },
+  [META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_cnts_use_reg_passwd] = { META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_cnts_use_reg_passwd, '3', XSIZE(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, cnts_use_reg_passwd), "cnts_use_reg_passwd", XOFFSET(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, cnts_use_reg_passwd) },
+  [META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_cnts_null_passwd] = { META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_cnts_null_passwd, '3', XSIZE(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, cnts_null_passwd), "cnts_null_passwd", XOFFSET(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, cnts_null_passwd) },
+  [META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_cnts_random_passwd] = { META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_cnts_random_passwd, '3', XSIZE(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, cnts_random_passwd), "cnts_random_passwd", XOFFSET(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, cnts_random_passwd) },
+  [META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_cnts_sha1] = { META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_cnts_sha1, '3', XSIZE(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, cnts_sha1), "cnts_sha1", XOFFSET(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, cnts_sha1) },
+  [META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_cnts_name_template] = { META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_cnts_name_template, '2', XSIZE(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, cnts_name_template), "cnts_name_template", XOFFSET(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, cnts_name_template) },
+  [META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_group_create] = { META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_group_create, '3', XSIZE(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, group_create), "group_create", XOFFSET(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, group_create) },
+  [META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_other_group_id] = { META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_other_group_id, '0', XSIZE(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, other_group_id), "other_group_id", XOFFSET(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, other_group_id) },
+  [META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_separator] = { META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_separator, '2', XSIZE(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, separator), "separator", XOFFSET(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, separator) },
+  [META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_charset] = { META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_charset, '2', XSIZE(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, charset), "charset", XOFFSET(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION, charset) },
+};
+
+int meta_ss_op_param_USER_CREATE_FROM_CSV_ACTION_get_type(int tag)
+{
+  ASSERT(tag > 0 && tag < META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_LAST_FIELD);
+  return meta_info_ss_op_param_USER_CREATE_FROM_CSV_ACTION_data[tag].type;
+}
+
+size_t meta_ss_op_param_USER_CREATE_FROM_CSV_ACTION_get_size(int tag)
+{
+  ASSERT(tag > 0 && tag < META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_LAST_FIELD);
+  return meta_info_ss_op_param_USER_CREATE_FROM_CSV_ACTION_data[tag].size;
+}
+
+const char *meta_ss_op_param_USER_CREATE_FROM_CSV_ACTION_get_name(int tag)
+{
+  ASSERT(tag > 0 && tag < META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_LAST_FIELD);
+  return meta_info_ss_op_param_USER_CREATE_FROM_CSV_ACTION_data[tag].name;
+}
+
+const void *meta_ss_op_param_USER_CREATE_FROM_CSV_ACTION_get_ptr(const struct ss_op_param_USER_CREATE_FROM_CSV_ACTION *ptr, int tag)
+{
+  ASSERT(tag > 0 && tag < META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_LAST_FIELD);
+  return XPDEREF(void, ptr, meta_info_ss_op_param_USER_CREATE_FROM_CSV_ACTION_data[tag].offset);
+}
+
+void *meta_ss_op_param_USER_CREATE_FROM_CSV_ACTION_get_ptr_nc(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION *ptr, int tag)
+{
+  ASSERT(tag > 0 && tag < META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_LAST_FIELD);
+  return XPDEREF(void, ptr, meta_info_ss_op_param_USER_CREATE_FROM_CSV_ACTION_data[tag].offset);
+}
+
+int meta_ss_op_param_USER_CREATE_FROM_CSV_ACTION_lookup_field(const char *name)
+{
+  static struct meta_automaton *atm = 0;
+  ASSERT(name);
+  if (!atm) atm = meta_build_automaton(meta_info_ss_op_param_USER_CREATE_FROM_CSV_ACTION_data, META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_LAST_FIELD);
+  return meta_lookup_string(atm, name);
+}
+
+const struct meta_methods meta_ss_op_param_USER_CREATE_FROM_CSV_ACTION_methods =
+{
+  META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_LAST_FIELD,
+  sizeof(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION),
+  meta_ss_op_param_USER_CREATE_FROM_CSV_ACTION_get_type,
+  meta_ss_op_param_USER_CREATE_FROM_CSV_ACTION_get_size,
+  meta_ss_op_param_USER_CREATE_FROM_CSV_ACTION_get_name,
+  (const void *(*)(const void *ptr, int tag))meta_ss_op_param_USER_CREATE_FROM_CSV_ACTION_get_ptr,
+  (void *(*)(void *ptr, int tag))meta_ss_op_param_USER_CREATE_FROM_CSV_ACTION_get_ptr_nc,
+  meta_ss_op_param_USER_CREATE_FROM_CSV_ACTION_lookup_field,
+};
+
+static struct meta_info_item meta_info_ss_op_param_USER_CREATE_REG_ACTION_data[] =
+{
+  [META_SS_OP_PARAM_USER_CREATE_REG_ACTION_other_user_id] = { META_SS_OP_PARAM_USER_CREATE_REG_ACTION_other_user_id, '0', XSIZE(struct ss_op_param_USER_CREATE_REG_ACTION, other_user_id), "other_user_id", XOFFSET(struct ss_op_param_USER_CREATE_REG_ACTION, other_user_id) },
+  [META_SS_OP_PARAM_USER_CREATE_REG_ACTION_contest_id] = { META_SS_OP_PARAM_USER_CREATE_REG_ACTION_contest_id, '0', XSIZE(struct ss_op_param_USER_CREATE_REG_ACTION, contest_id), "contest_id", XOFFSET(struct ss_op_param_USER_CREATE_REG_ACTION, contest_id) },
+  [META_SS_OP_PARAM_USER_CREATE_REG_ACTION_group_id] = { META_SS_OP_PARAM_USER_CREATE_REG_ACTION_group_id, '0', XSIZE(struct ss_op_param_USER_CREATE_REG_ACTION, group_id), "group_id", XOFFSET(struct ss_op_param_USER_CREATE_REG_ACTION, group_id) },
+  [META_SS_OP_PARAM_USER_CREATE_REG_ACTION_other_contest_id_1] = { META_SS_OP_PARAM_USER_CREATE_REG_ACTION_other_contest_id_1, '0', XSIZE(struct ss_op_param_USER_CREATE_REG_ACTION, other_contest_id_1), "other_contest_id_1", XOFFSET(struct ss_op_param_USER_CREATE_REG_ACTION, other_contest_id_1) },
+  [META_SS_OP_PARAM_USER_CREATE_REG_ACTION_status] = { META_SS_OP_PARAM_USER_CREATE_REG_ACTION_status, '4', XSIZE(struct ss_op_param_USER_CREATE_REG_ACTION, status), "status", XOFFSET(struct ss_op_param_USER_CREATE_REG_ACTION, status) },
+  [META_SS_OP_PARAM_USER_CREATE_REG_ACTION_is_invisible] = { META_SS_OP_PARAM_USER_CREATE_REG_ACTION_is_invisible, '3', XSIZE(struct ss_op_param_USER_CREATE_REG_ACTION, is_invisible), "is_invisible", XOFFSET(struct ss_op_param_USER_CREATE_REG_ACTION, is_invisible) },
+  [META_SS_OP_PARAM_USER_CREATE_REG_ACTION_is_banned] = { META_SS_OP_PARAM_USER_CREATE_REG_ACTION_is_banned, '3', XSIZE(struct ss_op_param_USER_CREATE_REG_ACTION, is_banned), "is_banned", XOFFSET(struct ss_op_param_USER_CREATE_REG_ACTION, is_banned) },
+  [META_SS_OP_PARAM_USER_CREATE_REG_ACTION_is_locked] = { META_SS_OP_PARAM_USER_CREATE_REG_ACTION_is_locked, '3', XSIZE(struct ss_op_param_USER_CREATE_REG_ACTION, is_locked), "is_locked", XOFFSET(struct ss_op_param_USER_CREATE_REG_ACTION, is_locked) },
+  [META_SS_OP_PARAM_USER_CREATE_REG_ACTION_is_incomplete] = { META_SS_OP_PARAM_USER_CREATE_REG_ACTION_is_incomplete, '3', XSIZE(struct ss_op_param_USER_CREATE_REG_ACTION, is_incomplete), "is_incomplete", XOFFSET(struct ss_op_param_USER_CREATE_REG_ACTION, is_incomplete) },
+  [META_SS_OP_PARAM_USER_CREATE_REG_ACTION_is_disqualified] = { META_SS_OP_PARAM_USER_CREATE_REG_ACTION_is_disqualified, '3', XSIZE(struct ss_op_param_USER_CREATE_REG_ACTION, is_disqualified), "is_disqualified", XOFFSET(struct ss_op_param_USER_CREATE_REG_ACTION, is_disqualified) },
+};
+
+int meta_ss_op_param_USER_CREATE_REG_ACTION_get_type(int tag)
+{
+  ASSERT(tag > 0 && tag < META_SS_OP_PARAM_USER_CREATE_REG_ACTION_LAST_FIELD);
+  return meta_info_ss_op_param_USER_CREATE_REG_ACTION_data[tag].type;
+}
+
+size_t meta_ss_op_param_USER_CREATE_REG_ACTION_get_size(int tag)
+{
+  ASSERT(tag > 0 && tag < META_SS_OP_PARAM_USER_CREATE_REG_ACTION_LAST_FIELD);
+  return meta_info_ss_op_param_USER_CREATE_REG_ACTION_data[tag].size;
+}
+
+const char *meta_ss_op_param_USER_CREATE_REG_ACTION_get_name(int tag)
+{
+  ASSERT(tag > 0 && tag < META_SS_OP_PARAM_USER_CREATE_REG_ACTION_LAST_FIELD);
+  return meta_info_ss_op_param_USER_CREATE_REG_ACTION_data[tag].name;
+}
+
+const void *meta_ss_op_param_USER_CREATE_REG_ACTION_get_ptr(const struct ss_op_param_USER_CREATE_REG_ACTION *ptr, int tag)
+{
+  ASSERT(tag > 0 && tag < META_SS_OP_PARAM_USER_CREATE_REG_ACTION_LAST_FIELD);
+  return XPDEREF(void, ptr, meta_info_ss_op_param_USER_CREATE_REG_ACTION_data[tag].offset);
+}
+
+void *meta_ss_op_param_USER_CREATE_REG_ACTION_get_ptr_nc(struct ss_op_param_USER_CREATE_REG_ACTION *ptr, int tag)
+{
+  ASSERT(tag > 0 && tag < META_SS_OP_PARAM_USER_CREATE_REG_ACTION_LAST_FIELD);
+  return XPDEREF(void, ptr, meta_info_ss_op_param_USER_CREATE_REG_ACTION_data[tag].offset);
+}
+
+int meta_ss_op_param_USER_CREATE_REG_ACTION_lookup_field(const char *name)
+{
+  static struct meta_automaton *atm = 0;
+  ASSERT(name);
+  if (!atm) atm = meta_build_automaton(meta_info_ss_op_param_USER_CREATE_REG_ACTION_data, META_SS_OP_PARAM_USER_CREATE_REG_ACTION_LAST_FIELD);
+  return meta_lookup_string(atm, name);
+}
+
+const struct meta_methods meta_ss_op_param_USER_CREATE_REG_ACTION_methods =
+{
+  META_SS_OP_PARAM_USER_CREATE_REG_ACTION_LAST_FIELD,
+  sizeof(struct ss_op_param_USER_CREATE_REG_ACTION),
+  meta_ss_op_param_USER_CREATE_REG_ACTION_get_type,
+  meta_ss_op_param_USER_CREATE_REG_ACTION_get_size,
+  meta_ss_op_param_USER_CREATE_REG_ACTION_get_name,
+  (const void *(*)(const void *ptr, int tag))meta_ss_op_param_USER_CREATE_REG_ACTION_get_ptr,
+  (void *(*)(void *ptr, int tag))meta_ss_op_param_USER_CREATE_REG_ACTION_get_ptr_nc,
+  meta_ss_op_param_USER_CREATE_REG_ACTION_lookup_field,
+};
+
+static struct meta_info_item meta_info_ss_op_param_USER_EDIT_REG_ACTION_data[] =
+{
+  [META_SS_OP_PARAM_USER_EDIT_REG_ACTION_other_user_id] = { META_SS_OP_PARAM_USER_EDIT_REG_ACTION_other_user_id, '0', XSIZE(struct ss_op_param_USER_EDIT_REG_ACTION, other_user_id), "other_user_id", XOFFSET(struct ss_op_param_USER_EDIT_REG_ACTION, other_user_id) },
+  [META_SS_OP_PARAM_USER_EDIT_REG_ACTION_other_contest_id] = { META_SS_OP_PARAM_USER_EDIT_REG_ACTION_other_contest_id, '0', XSIZE(struct ss_op_param_USER_EDIT_REG_ACTION, other_contest_id), "other_contest_id", XOFFSET(struct ss_op_param_USER_EDIT_REG_ACTION, other_contest_id) },
+  [META_SS_OP_PARAM_USER_EDIT_REG_ACTION_contest_id] = { META_SS_OP_PARAM_USER_EDIT_REG_ACTION_contest_id, '0', XSIZE(struct ss_op_param_USER_EDIT_REG_ACTION, contest_id), "contest_id", XOFFSET(struct ss_op_param_USER_EDIT_REG_ACTION, contest_id) },
+  [META_SS_OP_PARAM_USER_EDIT_REG_ACTION_group_id] = { META_SS_OP_PARAM_USER_EDIT_REG_ACTION_group_id, '0', XSIZE(struct ss_op_param_USER_EDIT_REG_ACTION, group_id), "group_id", XOFFSET(struct ss_op_param_USER_EDIT_REG_ACTION, group_id) },
+  [META_SS_OP_PARAM_USER_EDIT_REG_ACTION_next_op] = { META_SS_OP_PARAM_USER_EDIT_REG_ACTION_next_op, '0', XSIZE(struct ss_op_param_USER_EDIT_REG_ACTION, next_op), "next_op", XOFFSET(struct ss_op_param_USER_EDIT_REG_ACTION, next_op) },
+  [META_SS_OP_PARAM_USER_EDIT_REG_ACTION_status] = { META_SS_OP_PARAM_USER_EDIT_REG_ACTION_status, '4', XSIZE(struct ss_op_param_USER_EDIT_REG_ACTION, status), "status", XOFFSET(struct ss_op_param_USER_EDIT_REG_ACTION, status) },
+  [META_SS_OP_PARAM_USER_EDIT_REG_ACTION_is_invisible] = { META_SS_OP_PARAM_USER_EDIT_REG_ACTION_is_invisible, '3', XSIZE(struct ss_op_param_USER_EDIT_REG_ACTION, is_invisible), "is_invisible", XOFFSET(struct ss_op_param_USER_EDIT_REG_ACTION, is_invisible) },
+  [META_SS_OP_PARAM_USER_EDIT_REG_ACTION_is_banned] = { META_SS_OP_PARAM_USER_EDIT_REG_ACTION_is_banned, '3', XSIZE(struct ss_op_param_USER_EDIT_REG_ACTION, is_banned), "is_banned", XOFFSET(struct ss_op_param_USER_EDIT_REG_ACTION, is_banned) },
+  [META_SS_OP_PARAM_USER_EDIT_REG_ACTION_is_locked] = { META_SS_OP_PARAM_USER_EDIT_REG_ACTION_is_locked, '3', XSIZE(struct ss_op_param_USER_EDIT_REG_ACTION, is_locked), "is_locked", XOFFSET(struct ss_op_param_USER_EDIT_REG_ACTION, is_locked) },
+  [META_SS_OP_PARAM_USER_EDIT_REG_ACTION_is_incomplete] = { META_SS_OP_PARAM_USER_EDIT_REG_ACTION_is_incomplete, '3', XSIZE(struct ss_op_param_USER_EDIT_REG_ACTION, is_incomplete), "is_incomplete", XOFFSET(struct ss_op_param_USER_EDIT_REG_ACTION, is_incomplete) },
+  [META_SS_OP_PARAM_USER_EDIT_REG_ACTION_is_disqualified] = { META_SS_OP_PARAM_USER_EDIT_REG_ACTION_is_disqualified, '3', XSIZE(struct ss_op_param_USER_EDIT_REG_ACTION, is_disqualified), "is_disqualified", XOFFSET(struct ss_op_param_USER_EDIT_REG_ACTION, is_disqualified) },
+};
+
+int meta_ss_op_param_USER_EDIT_REG_ACTION_get_type(int tag)
+{
+  ASSERT(tag > 0 && tag < META_SS_OP_PARAM_USER_EDIT_REG_ACTION_LAST_FIELD);
+  return meta_info_ss_op_param_USER_EDIT_REG_ACTION_data[tag].type;
+}
+
+size_t meta_ss_op_param_USER_EDIT_REG_ACTION_get_size(int tag)
+{
+  ASSERT(tag > 0 && tag < META_SS_OP_PARAM_USER_EDIT_REG_ACTION_LAST_FIELD);
+  return meta_info_ss_op_param_USER_EDIT_REG_ACTION_data[tag].size;
+}
+
+const char *meta_ss_op_param_USER_EDIT_REG_ACTION_get_name(int tag)
+{
+  ASSERT(tag > 0 && tag < META_SS_OP_PARAM_USER_EDIT_REG_ACTION_LAST_FIELD);
+  return meta_info_ss_op_param_USER_EDIT_REG_ACTION_data[tag].name;
+}
+
+const void *meta_ss_op_param_USER_EDIT_REG_ACTION_get_ptr(const struct ss_op_param_USER_EDIT_REG_ACTION *ptr, int tag)
+{
+  ASSERT(tag > 0 && tag < META_SS_OP_PARAM_USER_EDIT_REG_ACTION_LAST_FIELD);
+  return XPDEREF(void, ptr, meta_info_ss_op_param_USER_EDIT_REG_ACTION_data[tag].offset);
+}
+
+void *meta_ss_op_param_USER_EDIT_REG_ACTION_get_ptr_nc(struct ss_op_param_USER_EDIT_REG_ACTION *ptr, int tag)
+{
+  ASSERT(tag > 0 && tag < META_SS_OP_PARAM_USER_EDIT_REG_ACTION_LAST_FIELD);
+  return XPDEREF(void, ptr, meta_info_ss_op_param_USER_EDIT_REG_ACTION_data[tag].offset);
+}
+
+int meta_ss_op_param_USER_EDIT_REG_ACTION_lookup_field(const char *name)
+{
+  static struct meta_automaton *atm = 0;
+  ASSERT(name);
+  if (!atm) atm = meta_build_automaton(meta_info_ss_op_param_USER_EDIT_REG_ACTION_data, META_SS_OP_PARAM_USER_EDIT_REG_ACTION_LAST_FIELD);
+  return meta_lookup_string(atm, name);
+}
+
+const struct meta_methods meta_ss_op_param_USER_EDIT_REG_ACTION_methods =
+{
+  META_SS_OP_PARAM_USER_EDIT_REG_ACTION_LAST_FIELD,
+  sizeof(struct ss_op_param_USER_EDIT_REG_ACTION),
+  meta_ss_op_param_USER_EDIT_REG_ACTION_get_type,
+  meta_ss_op_param_USER_EDIT_REG_ACTION_get_size,
+  meta_ss_op_param_USER_EDIT_REG_ACTION_get_name,
+  (const void *(*)(const void *ptr, int tag))meta_ss_op_param_USER_EDIT_REG_ACTION_get_ptr,
+  (void *(*)(void *ptr, int tag))meta_ss_op_param_USER_EDIT_REG_ACTION_get_ptr_nc,
+  meta_ss_op_param_USER_EDIT_REG_ACTION_lookup_field,
+};
+
diff -ruNp ejudge-2.3.18/super_html_6_meta.h ejudge/super_html_6_meta.h
--- ejudge-2.3.18/super_html_6_meta.h	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/super_html_6_meta.h	2011-07-11 09:02:04.371276561 +0400
@@ -0,0 +1,223 @@
+// This is an auto-generated file, do not edit
+// Generated 2011/05/22 11:43:29
+
+#ifndef __SUPER_HTML_6_META_H__
+#define __SUPER_HTML_6_META_H__
+
+#include <stdlib.h>
+
+enum
+{
+  META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_contest_id = 1,
+  META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_group_id,
+  META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_other_login,
+  META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_other_email,
+  META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_send_email,
+  META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_confirm_email,
+  META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_reg_password1,
+  META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_reg_password2,
+  META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_reg_sha1,
+  META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_field_1,
+  META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_field_2,
+  META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_field_3,
+  META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_field_4,
+  META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_field_5,
+  META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_field_6,
+  META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_field_7,
+  META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_field_8,
+  META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_field_9,
+  META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_reg_cnts_create,
+  META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_other_contest_id_1,
+  META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_cnts_status,
+  META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_is_invisible,
+  META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_is_banned,
+  META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_is_locked,
+  META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_is_incomplete,
+  META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_is_disqualified,
+  META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_cnts_use_reg_passwd,
+  META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_cnts_null_passwd,
+  META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_cnts_password1,
+  META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_cnts_password2,
+  META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_cnts_sha1,
+  META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_cnts_name,
+  META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_group_create,
+  META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_other_group_id,
+
+  META_SS_OP_PARAM_USER_CREATE_ONE_ACTION_LAST_FIELD,
+};
+
+struct ss_op_param_USER_CREATE_ONE_ACTION;
+
+int meta_ss_op_param_USER_CREATE_ONE_ACTION_get_type(int tag);
+size_t meta_ss_op_param_USER_CREATE_ONE_ACTION_get_size(int tag);
+const char *meta_ss_op_param_USER_CREATE_ONE_ACTION_get_name(int tag);
+const void *meta_ss_op_param_USER_CREATE_ONE_ACTION_get_ptr(const struct ss_op_param_USER_CREATE_ONE_ACTION *ptr, int tag);
+void *meta_ss_op_param_USER_CREATE_ONE_ACTION_get_ptr_nc(struct ss_op_param_USER_CREATE_ONE_ACTION *ptr, int tag);
+int meta_ss_op_param_USER_CREATE_ONE_ACTION_lookup_field(const char *name);
+
+struct meta_methods;
+extern const struct meta_methods meta_ss_op_param_USER_CREATE_ONE_ACTION_methods;
+
+
+enum
+{
+  META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_contest_id = 1,
+  META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_group_id,
+  META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_first_serial,
+  META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_last_serial,
+  META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_login_template,
+  META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_reg_random,
+  META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_reg_password_template,
+  META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_reg_sha1,
+  META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_field_1,
+  META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_field_2,
+  META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_field_3,
+  META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_field_4,
+  META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_field_5,
+  META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_field_6,
+  META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_field_7,
+  META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_field_8,
+  META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_field_9,
+  META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_reg_cnts_create,
+  META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_other_contest_id_1,
+  META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_cnts_status,
+  META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_is_invisible,
+  META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_is_banned,
+  META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_is_locked,
+  META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_is_incomplete,
+  META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_is_disqualified,
+  META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_cnts_use_reg_passwd,
+  META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_cnts_null_passwd,
+  META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_cnts_random_passwd,
+  META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_cnts_password_template,
+  META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_cnts_sha1,
+  META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_cnts_name_template,
+  META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_group_create,
+  META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_other_group_id,
+
+  META_SS_OP_PARAM_USER_CREATE_MANY_ACTION_LAST_FIELD,
+};
+
+struct ss_op_param_USER_CREATE_MANY_ACTION;
+
+int meta_ss_op_param_USER_CREATE_MANY_ACTION_get_type(int tag);
+size_t meta_ss_op_param_USER_CREATE_MANY_ACTION_get_size(int tag);
+const char *meta_ss_op_param_USER_CREATE_MANY_ACTION_get_name(int tag);
+const void *meta_ss_op_param_USER_CREATE_MANY_ACTION_get_ptr(const struct ss_op_param_USER_CREATE_MANY_ACTION *ptr, int tag);
+void *meta_ss_op_param_USER_CREATE_MANY_ACTION_get_ptr_nc(struct ss_op_param_USER_CREATE_MANY_ACTION *ptr, int tag);
+int meta_ss_op_param_USER_CREATE_MANY_ACTION_lookup_field(const char *name);
+
+struct meta_methods;
+extern const struct meta_methods meta_ss_op_param_USER_CREATE_MANY_ACTION_methods;
+
+
+enum
+{
+  META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_contest_id = 1,
+  META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_group_id,
+  META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_send_email,
+  META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_confirm_email,
+  META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_reg_random,
+  META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_reg_sha1,
+  META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_field_1,
+  META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_field_2,
+  META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_field_3,
+  META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_field_4,
+  META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_field_5,
+  META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_field_6,
+  META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_field_7,
+  META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_field_8,
+  META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_field_9,
+  META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_reg_cnts_create,
+  META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_other_contest_id_1,
+  META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_cnts_status,
+  META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_is_invisible,
+  META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_is_banned,
+  META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_is_locked,
+  META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_is_incomplete,
+  META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_is_disqualified,
+  META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_cnts_use_reg_passwd,
+  META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_cnts_null_passwd,
+  META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_cnts_random_passwd,
+  META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_cnts_sha1,
+  META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_cnts_name_template,
+  META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_group_create,
+  META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_other_group_id,
+  META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_separator,
+  META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_charset,
+
+  META_SS_OP_PARAM_USER_CREATE_FROM_CSV_ACTION_LAST_FIELD,
+};
+
+struct ss_op_param_USER_CREATE_FROM_CSV_ACTION;
+
+int meta_ss_op_param_USER_CREATE_FROM_CSV_ACTION_get_type(int tag);
+size_t meta_ss_op_param_USER_CREATE_FROM_CSV_ACTION_get_size(int tag);
+const char *meta_ss_op_param_USER_CREATE_FROM_CSV_ACTION_get_name(int tag);
+const void *meta_ss_op_param_USER_CREATE_FROM_CSV_ACTION_get_ptr(const struct ss_op_param_USER_CREATE_FROM_CSV_ACTION *ptr, int tag);
+void *meta_ss_op_param_USER_CREATE_FROM_CSV_ACTION_get_ptr_nc(struct ss_op_param_USER_CREATE_FROM_CSV_ACTION *ptr, int tag);
+int meta_ss_op_param_USER_CREATE_FROM_CSV_ACTION_lookup_field(const char *name);
+
+struct meta_methods;
+extern const struct meta_methods meta_ss_op_param_USER_CREATE_FROM_CSV_ACTION_methods;
+
+
+enum
+{
+  META_SS_OP_PARAM_USER_CREATE_REG_ACTION_other_user_id = 1,
+  META_SS_OP_PARAM_USER_CREATE_REG_ACTION_contest_id,
+  META_SS_OP_PARAM_USER_CREATE_REG_ACTION_group_id,
+  META_SS_OP_PARAM_USER_CREATE_REG_ACTION_other_contest_id_1,
+  META_SS_OP_PARAM_USER_CREATE_REG_ACTION_status,
+  META_SS_OP_PARAM_USER_CREATE_REG_ACTION_is_invisible,
+  META_SS_OP_PARAM_USER_CREATE_REG_ACTION_is_banned,
+  META_SS_OP_PARAM_USER_CREATE_REG_ACTION_is_locked,
+  META_SS_OP_PARAM_USER_CREATE_REG_ACTION_is_incomplete,
+  META_SS_OP_PARAM_USER_CREATE_REG_ACTION_is_disqualified,
+
+  META_SS_OP_PARAM_USER_CREATE_REG_ACTION_LAST_FIELD,
+};
+
+struct ss_op_param_USER_CREATE_REG_ACTION;
+
+int meta_ss_op_param_USER_CREATE_REG_ACTION_get_type(int tag);
+size_t meta_ss_op_param_USER_CREATE_REG_ACTION_get_size(int tag);
+const char *meta_ss_op_param_USER_CREATE_REG_ACTION_get_name(int tag);
+const void *meta_ss_op_param_USER_CREATE_REG_ACTION_get_ptr(const struct ss_op_param_USER_CREATE_REG_ACTION *ptr, int tag);
+void *meta_ss_op_param_USER_CREATE_REG_ACTION_get_ptr_nc(struct ss_op_param_USER_CREATE_REG_ACTION *ptr, int tag);
+int meta_ss_op_param_USER_CREATE_REG_ACTION_lookup_field(const char *name);
+
+struct meta_methods;
+extern const struct meta_methods meta_ss_op_param_USER_CREATE_REG_ACTION_methods;
+
+
+enum
+{
+  META_SS_OP_PARAM_USER_EDIT_REG_ACTION_other_user_id = 1,
+  META_SS_OP_PARAM_USER_EDIT_REG_ACTION_other_contest_id,
+  META_SS_OP_PARAM_USER_EDIT_REG_ACTION_contest_id,
+  META_SS_OP_PARAM_USER_EDIT_REG_ACTION_group_id,
+  META_SS_OP_PARAM_USER_EDIT_REG_ACTION_next_op,
+  META_SS_OP_PARAM_USER_EDIT_REG_ACTION_status,
+  META_SS_OP_PARAM_USER_EDIT_REG_ACTION_is_invisible,
+  META_SS_OP_PARAM_USER_EDIT_REG_ACTION_is_banned,
+  META_SS_OP_PARAM_USER_EDIT_REG_ACTION_is_locked,
+  META_SS_OP_PARAM_USER_EDIT_REG_ACTION_is_incomplete,
+  META_SS_OP_PARAM_USER_EDIT_REG_ACTION_is_disqualified,
+
+  META_SS_OP_PARAM_USER_EDIT_REG_ACTION_LAST_FIELD,
+};
+
+struct ss_op_param_USER_EDIT_REG_ACTION;
+
+int meta_ss_op_param_USER_EDIT_REG_ACTION_get_type(int tag);
+size_t meta_ss_op_param_USER_EDIT_REG_ACTION_get_size(int tag);
+const char *meta_ss_op_param_USER_EDIT_REG_ACTION_get_name(int tag);
+const void *meta_ss_op_param_USER_EDIT_REG_ACTION_get_ptr(const struct ss_op_param_USER_EDIT_REG_ACTION *ptr, int tag);
+void *meta_ss_op_param_USER_EDIT_REG_ACTION_get_ptr_nc(struct ss_op_param_USER_EDIT_REG_ACTION *ptr, int tag);
+int meta_ss_op_param_USER_EDIT_REG_ACTION_lookup_field(const char *name);
+
+struct meta_methods;
+extern const struct meta_methods meta_ss_op_param_USER_EDIT_REG_ACTION_methods;
+
+#endif
diff -ruNp ejudge-2.3.18/super_html.c ejudge/super_html.c
--- ejudge-2.3.18/super_html.c	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/super_html.c	2011-07-11 09:02:04.752269556 +0400
@@ -1,5 +1,5 @@
 /* -*- mode: c -*- */
-/* $Id: super_html.c 6242 2011-04-10 05:18:38Z cher $ */
+/* $Id: super_html.c 6390 2011-06-30 19:53:38Z cher $ */
 
 /* Copyright (C) 2004-2011 Alexander Chernov <cher@ejudge.ru> */
 
@@ -133,34 +133,6 @@ enum
   MNG_STAT_LAST = MNG_STAT_WAITING,
 };
 static int
-get_serve_management_status(const struct contest_desc *cnts,
-                            struct contest_extra *extra)
-{
-  if (cnts->managed) {
-    return MNG_STAT_NEW_MANAGED;
-  } else if (!cnts->managed && (!extra || !extra->serve_used)) {
-    return MNG_STAT_NOT_MANAGED;
-  } else if (!extra || !extra->serve_used) {
-    return MNG_STAT_TEMP_NOT_MANAGED;
-  } else if (!cnts->managed) {
-    if (extra->serve_suspended) {
-      return MNG_STAT_TEMP_FAILED;
-    } else if (extra->serve_pid > 0) {
-      return MNG_STAT_TEMP_RUNNING;
-    } else {
-      return MNG_STAT_TEMP_WAITING;
-    }
-  } else {
-    if (extra->serve_suspended) {
-      return MNG_STAT_FAILED;
-    } else if (extra->serve_pid > 0) {
-      return MNG_STAT_RUNNING;
-    } else {
-      return MNG_STAT_WAITING;
-    }
-  }
-}
-static int
 get_run_management_status(const struct contest_desc *cnts,
                           struct contest_extra *extra)
 {
@@ -390,6 +362,9 @@ super_html_main_page(FILE *f,
 
   fprintf(f, "<table border=\"0\"><tr><td>%sProblem editor</a></td></tr></table>\n", html_hyperref(hbuf, sizeof(hbuf), session_id, self_url, extra_args, "action=%d&op=%d", SSERV_CMD_HTTP_REQUEST, SSERV_OP_BROWSE_PROBLEM_PACKAGES));
 
+  fprintf(f, "<table border=\"0\"><tr><td>%sUser editor</a></td></tr></table>\n", html_hyperref(hbuf, sizeof(hbuf), session_id, self_url, extra_args, "action=%d&op=%d", SSERV_CMD_HTTP_REQUEST, SSERV_OP_USER_BROWSE_PAGE));
+  fprintf(f, "<table border=\"0\"><tr><td>%sGroup editor</a></td></tr></table>\n", html_hyperref(hbuf, sizeof(hbuf), session_id, self_url, extra_args, "action=%d&op=%d", SSERV_CMD_HTTP_REQUEST, SSERV_OP_GROUP_BROWSE_PAGE));
+
   fprintf(f, "<table border=\"0\"><tr><td>%sCreate new contest</a></td>", html_hyperref(hbuf, sizeof(hbuf), session_id, self_url, extra_args, "action=%d", SSERV_CMD_CREATE_CONTEST));
   if (sstate->edited_cnts) {
     fprintf(f, "<td>%sEdit current contest</a></td>",
@@ -423,9 +398,11 @@ super_html_main_page(FILE *f,
           "<th>Name</th>\n"
           "<th>Closed?</th>\n"
           "<th>Run status</th>\n"
+          "<th>Users</th>\n"
           "<th>Judge</th>\n"
           "<th>Master</th>\n"
           "<th>User</th>\n"
+          "<th>Edit</th>\n"
           "<th>Details</th>\n"
           "</tr>\n");
   for (contest_id = 1; contest_id < contest_max_id; contest_id++) {
@@ -586,6 +563,8 @@ super_html_main_page(FILE *f,
       fprintf(f, "<td>&nbsp;</td>\n");
     }
 
+    fprintf(f, "<td>%sUsers</a></td>\n", html_hyperref(hbuf, sizeof(hbuf), session_id, self_url, extra_args, "action=%d&op=%d&contest_id=%d", SSERV_CMD_HTTP_REQUEST, SSERV_OP_USER_BROWSE_PAGE, contest_id));
+
     // report judge URL
     if (opcaps_check(caps, OPCAP_JUDGE_LOGIN) >= 0 && judge_url[0]
         && contests_check_judge_ip_2(cnts, ip_address, ssl)) {
@@ -628,6 +607,10 @@ super_html_main_page(FILE *f,
     if (priv_level >= PRIV_LEVEL_ADMIN
         && opcaps_check(caps, OPCAP_CONTROL_CONTEST) >= 0
         && contests_check_serve_control_ip_2(cnts, ip_address, ssl)) {
+      fprintf(f, "<td>%sEdit</a></td>",
+              html_hyperref(hbuf, sizeof(hbuf), session_id, self_url, extra_args,
+                            "contest_id=%d&action=%d", contest_id,
+                            SSERV_CMD_EDIT_CONTEST_XML));
       fprintf(f, "<td>%sDetails</a></td>\n",
               html_hyperref(hbuf, sizeof(hbuf), session_id, self_url, extra_args,
                             "contest_id=%d&action=%d", contest_id,
@@ -783,6 +766,8 @@ super_html_contest_page(FILE *f,
     fprintf(f, "<tr><td>Contest keywords:</td><td><tt>%s</tt></td></tr>\n", cnts->keywords);
   }
 
+  fprintf(f, "<tr><td>View/edit users</td><td>%sUsers</a></td></tr>\n", html_hyperref(hbuf, sizeof(hbuf), session_id, self_url, extra_args, "action=%d&op=%d&contest_id=%d", SSERV_CMD_HTTP_REQUEST, SSERV_OP_USER_BROWSE_PAGE, contest_id));
+
   // report judge URL
   if (opcaps_check(caps, OPCAP_JUDGE_LOGIN) >= 0 && judge_url[0]
       && contests_check_judge_ip_2(cnts, ip_address, ssl)) {
@@ -850,149 +835,6 @@ super_html_contest_page(FILE *f,
   }
   fprintf(f, "</tr>\n");
 
-  // report serve status
-  mng_status = get_serve_management_status(cnts, extra);
-  snprintf(mng_status_str, sizeof(mng_status_str),
-           mng_status_table[mng_status], extra?extra->serve_pid:0);
-  fprintf(f, "<tr><td><tt>serve</tt> management status:</td><td>%s</td>",
-          mng_status_str);
-  if (opcaps_check(caps, OPCAP_CONTROL_CONTEST) >= 0) {
-    fprintf(f, "<td>");
-    html_start_form(f, 1, self_url, new_hidden_vars);
-    switch (mng_status) {
-    case MNG_STAT_NOT_MANAGED:
-    case MNG_STAT_NEW_MANAGED:
-      fprintf(f, "&nbsp;");
-      /* FIXME: disabled for now
-      html_submit_button(f, SSERV_CMD_SERVE_MNG_TEMP,
-                         "Manage temporarily");
-      html_submit_button(f, SSERV_CMD_SERVE_MNG, "Manage permanently");
-      */
-      break;
-    case MNG_STAT_TEMP_NOT_MANAGED:
-      fprintf(f, "&nbsp;");
-      /*
-      html_submit_button(f, SSERV_CMD_SERVE_MNG_RESUME,
-                         "Resume management");
-      html_submit_button(f, SSERV_CMD_SERVE_MNG_STOP, "Stop management");
-      */
-      break;
-    case MNG_STAT_TEMP_FAILED:
-      /*
-      html_submit_button(f, SSERV_CMD_SERVE_MNG, "Manage permanently");
-      html_submit_button(f, SSERV_CMD_SERVE_MNG_STOP, "Stop management");
-      */
-      html_submit_button(f, SSERV_CMD_SERVE_MNG_RESET_ERROR,
-                         "Reset error flag");
-      html_submit_button(f, SSERV_CMD_SERVE_MNG_PROBE_RUN,
-                         "Do probe run");
-      break;
-    case MNG_STAT_TEMP_RUNNING:
-      /*
-      html_submit_button(f, SSERV_CMD_SERVE_MNG, "Manage permanently");
-      html_submit_button(f, SSERV_CMD_SERVE_MNG_STOP, "Stop management");
-      */
-      html_submit_button(f, SSERV_CMD_SERVE_MNG_TERM, "Terminate serve");
-      break;
-    case MNG_STAT_TEMP_WAITING:
-      /*
-      html_submit_button(f, SSERV_CMD_SERVE_MNG, "Manage permanently");
-      html_submit_button(f, SSERV_CMD_SERVE_MNG_STOP, "Stop management");
-      */
-      html_submit_button(f, SSERV_CMD_SERVE_MNG_PROBE_RUN,
-                         "Do probe run");
-      break;
-    case MNG_STAT_FAILED:
-      /*
-      html_submit_button(f, SSERV_CMD_SERVE_MNG_SUSPEND,
-                         "Suspend management");
-      html_submit_button(f, SSERV_CMD_SERVE_MNG_STOP, "Stop management");
-      */
-      html_submit_button(f, SSERV_CMD_SERVE_MNG_RESET_ERROR,
-                         "Reset error flag");
-      html_submit_button(f, SSERV_CMD_SERVE_MNG_PROBE_RUN,
-                         "Do probe run");
-      break;
-    case MNG_STAT_RUNNING:
-      /*
-      html_submit_button(f, SSERV_CMD_SERVE_MNG_SUSPEND,
-                         "Suspend management");
-      html_submit_button(f, SSERV_CMD_SERVE_MNG_STOP, "Stop management");
-      */
-      html_submit_button(f, SSERV_CMD_SERVE_MNG_TERM, "Terminate serve");
-      break;
-    case MNG_STAT_WAITING:
-      fprintf(f, "&nbsp;");
-      /*
-      html_submit_button(f, SSERV_CMD_SERVE_MNG_SUSPEND,
-                         "Suspend management");
-      html_submit_button(f, SSERV_CMD_SERVE_MNG_STOP,
-                         "Stop management");
-      */
-      html_submit_button(f, SSERV_CMD_SERVE_MNG_PROBE_RUN,
-                         "Do probe run");
-      break;
-    default:
-      abort();
-    }
-    fprintf(f, "</form>");
-    fprintf(f, "</td>");
-  }
-
-  // whether /dev/null exists?
-  if (stat("/dev/null", &devnullstat) < 0) {
-    // :( how come???
-    nodevnull = 1;
-  }
-
-  // serve log file status
-  if (!cnts->root_dir) {
-    logfilemode = 3;
-    snprintf(mng_status_str, sizeof(mng_status_str), "N/A");
-  } else {
-    snprintf(log_file_path, sizeof(log_file_path),
-             "%s/var/messages", cnts->root_dir);
-    if (stat(log_file_path, &logfilestat) < 0) {
-      logfilemode = 0;
-      snprintf(mng_status_str, sizeof(mng_status_str), "nonexistant");
-    } else if (!nodevnull
-               && logfilestat.st_dev == devnullstat.st_dev
-               && logfilestat.st_ino == devnullstat.st_ino) {
-      logfilemode = 1;
-      snprintf(mng_status_str, sizeof(mng_status_str), "/dev/null");
-    } else {
-      logfilemode = 2;
-      snprintf(mng_status_str, sizeof(mng_status_str), "%lld bytes",
-               (long long) logfilestat.st_size);
-    }
-  }
-
-  fprintf(f, "<tr><td>Serve log:</td><td>%s</td>", mng_status_str);
-  if (opcaps_check(caps, OPCAP_CONTROL_CONTEST) >= 0
-      && logfilemode != 3) {
-    fprintf(f, "<td>");
-    html_start_form(f, 1, self_url, new_hidden_vars);
-    if (logfilemode == 0) {
-      html_submit_button(f, SSERV_CMD_SERVE_LOG_DEV_NULL,
-                         "Redirect to /dev/null");
-    } else if (logfilemode == 2) {
-      html_submit_button(f, SSERV_CMD_SERVE_LOG_TRUNC, "Truncate log");
-      html_submit_button(f, SSERV_CMD_SERVE_LOG_DEV_NULL,
-                         "Redirect to /dev/null");
-      if (logfilestat.st_size <= MAX_LOG_VIEW_SIZE) {
-        fprintf(f, "%sView</a>",
-                html_hyperref(hbuf, sizeof(hbuf), session_id, self_url, extra_args,
-                              "contest_id=%d&action=%d", contest_id,
-                              SSERV_CMD_VIEW_SERVE_LOG));
-      }
-    } else if (logfilemode == 1) {
-      html_submit_button(f, SSERV_CMD_SERVE_LOG_FILE, "Redirect to file");
-    }
-    fprintf(f, "</form>");
-    fprintf(f, "</td>");
-  }
-  fprintf(f, "</tr>\n");
-
   // report run status
   mng_status = get_run_management_status(cnts, extra);
   snprintf(mng_status_str, sizeof(mng_status_str),
@@ -1092,7 +934,7 @@ super_html_contest_page(FILE *f,
     snprintf(mng_status_str, sizeof(mng_status_str), "N/A");
   } else {
     snprintf(log_file_path, sizeof(log_file_path),
-             "%s/var/run_messages", cnts->root_dir);
+             "%s/var/ej-run-messages.log", cnts->root_dir);
     if (stat(log_file_path, &logfilestat) < 0) {
       logfilemode = 0;
       snprintf(mng_status_str, sizeof(mng_status_str), "nonexistant");
@@ -1299,7 +1141,7 @@ super_html_log_page(FILE *f,
     break;
   case SSERV_CMD_VIEW_RUN_LOG:
     snprintf(log_file_path, sizeof(log_file_path),
-             "%s/var/run_messages", cnts->root_dir);
+             "%s/var/ej-run-messages.log", cnts->root_dir);
     progname = "run";
     refresh_action = SSERV_CMD_VIEW_RUN_LOG;
     break;
@@ -2461,6 +2303,26 @@ super_html_edit_contest_page(FILE *f,
                            self_url,
                            extra_args,
                            hidden_vars);
+  print_string_editing_row(f, "URL for the contest logo:",
+                           cnts->logo_url,
+                           SSERV_CMD_CNTS_CHANGE_LOGO_URL,
+                           SSERV_CMD_CNTS_CLEAR_LOGO_URL,
+                           0,
+                           session_id,
+                           form_row_attrs[row ^= 1],
+                           self_url,
+                           extra_args,
+                           hidden_vars);
+  print_string_editing_row(f, "URL for the contest CSS:",
+                           cnts->css_url,
+                           SSERV_CMD_CNTS_CHANGE_CSS_URL,
+                           SSERV_CMD_CNTS_CLEAR_CSS_URL,
+                           0,
+                           session_id,
+                           form_row_attrs[row ^= 1],
+                           self_url,
+                           extra_args,
+                           hidden_vars);
 
   html_start_form(f, 1, self_url, hidden_vars);
   fprintf(f, "<tr%s><td colspan=\"4\" align=\"center\"><b>Various contest's flags</b>", head_row_attr);
diff -ruNp ejudge-2.3.18/super_html.h ejudge/super_html.h
--- ejudge-2.3.18/super_html.h	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/super_html.h	2011-07-11 09:02:04.769269244 +0400
@@ -1,9 +1,9 @@
 /* -*- c -*- */
-/* $Id: super_html.h 5675 2010-01-19 09:52:11Z cher $ */
+/* $Id: super_html.h 6379 2011-06-24 18:47:55Z cher $ */
 #ifndef __SUPER_HTML_H__
 #define __SUPER_HTML_H__
 
-/* Copyright (C) 2004-2010 Alexander Chernov <cher@ejudge.ru> */
+/* Copyright (C) 2004-2011 Alexander Chernov <cher@ejudge.ru> */
 
 /*
  * This program is free software; you can redistribute it and/or modify
@@ -484,6 +484,34 @@ enum
   S_ERR_INV_PACKAGE,
   S_ERR_ITEM_EXISTS,
   S_ERR_OPERATION_FAILED,
+  S_ERR_INV_USER_ID,
+  S_ERR_NO_CONNECTION,
+  S_ERR_DB_ERROR,
+  S_ERR_UNSPEC_PASSWD1,
+  S_ERR_UNSPEC_PASSWD2,
+  S_ERR_INV_PASSWD1,
+  S_ERR_INV_PASSWD2,
+  S_ERR_PASSWDS_DIFFER,
+  S_ERR_UNSPEC_LOGIN,
+  S_ERR_DUPLICATED_LOGIN,
+  S_ERR_INV_GROUP_ID,
+  S_ERR_INV_FIRST_SERIAL,
+  S_ERR_INV_LAST_SERIAL,
+  S_ERR_INV_RANGE,
+  S_ERR_INV_LOGIN_TEMPLATE,
+  S_ERR_INV_REG_PASSWORD_TEMPLATE,
+  S_ERR_INV_CNTS_PASSWORD_TEMPLATE,
+  S_ERR_INV_CNTS_NAME_TEMPLATE,
+  S_ERR_INV_CSV_FILE,
+  S_ERR_INV_CHARSET,
+  S_ERR_INV_SEPARATOR,
+  S_ERR_DATA_READ_ONLY,
+  S_ERR_TOO_MANY_MEMBERS,
+  S_ERR_INV_SERIAL,
+  S_ERR_INV_EMAIL,
+  S_ERR_INV_GROUP_NAME,
+  S_ERR_INV_DESCRIPTION,
+  S_ERR_GROUP_CREATION_FAILED,
 
   S_ERR_LAST
 };
@@ -493,6 +521,17 @@ ss_cgi_param(
         const struct super_http_request_info *phr,
         const unsigned char *param,
         const unsigned char **p_value);
+int
+ss_cgi_param_int(
+        struct super_http_request_info *phr,
+        const unsigned char *name,
+        int *p_val);
+int
+ss_cgi_param_int_opt(
+        struct super_http_request_info *phr,
+        const unsigned char *name,
+        int *p_val,
+        int default_value);
 
 void
 ss_write_html_header(
diff -ruNp ejudge-2.3.18/super_proto.h ejudge/super_proto.h
--- ejudge-2.3.18/super_proto.h	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/super_proto.h	2011-07-11 09:02:04.520273822 +0400
@@ -1,5 +1,5 @@
 /* -*- c -*- */
-/* $Id: super_proto.h 6214 2011-04-01 20:03:14Z cher $ */
+/* $Id: super_proto.h 6379 2011-06-24 18:47:55Z cher $ */
 
 #ifndef __SUPER_PROTO_H__
 #define __SUPER_PROTO_H__
@@ -165,6 +165,8 @@ enum
   SSERV_CMD_CNTS_CLEAR_TEAM_URL,
   SSERV_CMD_CNTS_CLEAR_STANDINGS_URL,
   SSERV_CMD_CNTS_CLEAR_PROBLEMS_URL,
+  SSERV_CMD_CNTS_CLEAR_LOGO_URL,
+  SSERV_CMD_CNTS_CLEAR_CSS_URL,
   SSERV_CMD_CNTS_CLEAR_ROOT_DIR,
   SSERV_CMD_CNTS_CLEAR_CONF_DIR,
 
@@ -237,6 +239,8 @@ enum
   SSERV_CMD_CNTS_CHANGE_TEAM_URL,
   SSERV_CMD_CNTS_CHANGE_STANDINGS_URL,
   SSERV_CMD_CNTS_CHANGE_PROBLEMS_URL,
+  SSERV_CMD_CNTS_CHANGE_LOGO_URL,
+  SSERV_CMD_CNTS_CHANGE_CSS_URL,
   SSERV_CMD_CNTS_CHANGE_ROOT_DIR,
   SSERV_CMD_CNTS_CHANGE_CONF_DIR,
   SSERV_CMD_CNTS_CHANGE_DIR_MODE,
@@ -828,6 +832,88 @@ enum
   SSERV_OP_DELETE_ITEM,
   SSERV_OP_EDIT_PROBLEM,
 
+  SSERV_OP_USER_BROWSE_PAGE,                   /* OK */
+  SSERV_OP_USER_FILTER_CHANGE_ACTION,          /* OK */
+  SSERV_OP_USER_FILTER_FIRST_PAGE_ACTION,      /* OK */
+  SSERV_OP_USER_FILTER_PREV_PAGE_ACTION,       /* OK */
+  SSERV_OP_USER_FILTER_NEXT_PAGE_ACTION,       /* OK */
+  SSERV_OP_USER_FILTER_LAST_PAGE_ACTION,       /* OK */
+  SSERV_OP_USER_JUMP_CONTEST_ACTION,
+  SSERV_OP_USER_JUMP_GROUP_ACTION,
+  SSERV_OP_USER_BROWSE_MARK_ALL_ACTION,
+  SSERV_OP_USER_BROWSE_UNMARK_ALL_ACTION,
+  SSERV_OP_USER_BROWSE_TOGGLE_ALL_ACTION,
+  SSERV_OP_USER_CREATE_ONE_PAGE,               /* OK */
+  SSERV_OP_USER_CREATE_ONE_ACTION,             /* OK */
+  SSERV_OP_USER_CREATE_MANY_PAGE,              /* OK */
+  SSERV_OP_USER_CREATE_MANY_ACTION,            /* OK */
+  SSERV_OP_USER_CREATE_FROM_CSV_PAGE,          /* OK */
+  SSERV_OP_USER_CREATE_FROM_CSV_ACTION,        /* OK */
+  SSERV_OP_USER_DETAIL_PAGE,                   /* OK */
+  SSERV_OP_USER_PASSWORD_PAGE,                 /* OK */
+  SSERV_OP_USER_CHANGE_PASSWORD_ACTION,        /* OK */
+  SSERV_OP_USER_CNTS_PASSWORD_PAGE,            /* OK */
+  SSERV_OP_USER_CHANGE_CNTS_PASSWORD_ACTION,   /* OK */
+  SSERV_OP_USER_CLEAR_FIELD_ACTION,
+  SSERV_OP_USER_CREATE_MEMBER_ACTION,          /* OK */
+  SSERV_OP_USER_DELETE_MEMBER_PAGE,            /* OK */
+  SSERV_OP_USER_DELETE_MEMBER_ACTION,          /* OK */
+  SSERV_OP_USER_SAVE_AND_PREV_ACTION,          /* OK */
+  SSERV_OP_USER_SAVE_ACTION,                   /* OK */
+  SSERV_OP_USER_SAVE_AND_NEXT_ACTION,          /* OK */
+  SSERV_OP_USER_CANCEL_AND_PREV_ACTION,        /* OK */
+  SSERV_OP_USER_CANCEL_ACTION,                 /* OK */
+  SSERV_OP_USER_CANCEL_AND_NEXT_ACTION,        /* OK */
+  SSERV_OP_USER_CREATE_REG_PAGE,               /* OK */
+  SSERV_OP_USER_CREATE_REG_ACTION,             /* OK */
+  SSERV_OP_USER_EDIT_REG_PAGE,                 /* OK */
+  SSERV_OP_USER_EDIT_REG_ACTION,               /* OK */
+  SSERV_OP_USER_DELETE_REG_PAGE,               /* OK */
+  SSERV_OP_USER_DELETE_REG_ACTION,             /* OK */
+  SSERV_OP_USER_DELETE_SESSION_ACTION,
+  SSERV_OP_USER_DELETE_ALL_SESSIONS_ACTION,
+  SSERV_OP_USER_SEL_RANDOM_PASSWD_PAGE,
+  SSERV_OP_USER_SEL_RANDOM_PASSWD_ACTION,
+  SSERV_OP_USER_SEL_CLEAR_CNTS_PASSWD_PAGE,
+  SSERV_OP_USER_SEL_CLEAR_CNTS_PASSWD_ACTION,
+  SSERV_OP_USER_SEL_RANDOM_CNTS_PASSWD_PAGE,
+  SSERV_OP_USER_SEL_RANDOM_CNTS_PASSWD_ACTION,
+  SSERV_OP_USER_SEL_CREATE_REG_PAGE,
+  SSERV_OP_USER_SEL_CREATE_REG_ACTION,
+  SSERV_OP_USER_SEL_CREATE_REG_AND_COPY_PAGE,
+  SSERV_OP_USER_SEL_CREATE_REG_AND_COPY_ACTION,
+  SSERV_OP_USER_SEL_DELETE_REG_PAGE,
+  SSERV_OP_USER_SEL_DELETE_REG_ACTION,
+  SSERV_OP_USER_SEL_CHANGE_REG_STATUS_PAGE,
+  SSERV_OP_USER_SEL_CHANGE_REG_STATUS_ACTION,
+  SSERV_OP_USER_SEL_CHANGE_REG_FLAGS_PAGE,
+  SSERV_OP_USER_SEL_CHANGE_REG_FLAGS_ACTION,
+  SSERV_OP_USER_SEL_CANCEL_ACTION,
+  SSERV_OP_USER_SEL_VIEW_PASSWD_PAGE,
+  SSERV_OP_USER_SEL_VIEW_CNTS_PASSWD_PAGE,
+  SSERV_OP_USER_SEL_VIEW_PASSWD_REDIRECT,
+  SSERV_OP_USER_SEL_VIEW_CNTS_PASSWD_REDIRECT,
+  SSERV_OP_USER_SEL_CREATE_GROUP_MEMBER_PAGE,
+  SSERV_OP_USER_SEL_CREATE_GROUP_MEMBER_ACTION,
+  SSERV_OP_USER_SEL_DELETE_GROUP_MEMBER_PAGE,
+  SSERV_OP_USER_SEL_DELETE_GROUP_MEMBER_ACTION,
+
+  SSERV_OP_GROUP_BROWSE_PAGE,
+  SSERV_OP_GROUP_FILTER_CHANGE_ACTION,
+  SSERV_OP_GROUP_FILTER_FIRST_PAGE_ACTION,
+  SSERV_OP_GROUP_FILTER_PREV_PAGE_ACTION,
+  SSERV_OP_GROUP_FILTER_NEXT_PAGE_ACTION,
+  SSERV_OP_GROUP_FILTER_LAST_PAGE_ACTION,
+  SSERV_OP_GROUP_CREATE_PAGE,
+  SSERV_OP_GROUP_CREATE_ACTION,
+  SSERV_OP_GROUP_MODIFY_PAGE,
+  SSERV_OP_GROUP_MODIFY_PAGE_ACTION,
+  SSERV_OP_GROUP_MODIFY_ACTION,
+  SSERV_OP_GROUP_DELETE_PAGE,
+  SSERV_OP_GROUP_DELETE_PAGE_ACTION,
+  SSERV_OP_GROUP_DELETE_ACTION,
+  SSERV_OP_GROUP_CANCEL_ACTION,
+
   SSERV_OP_LAST,
 };
 
diff -ruNp ejudge-2.3.18/super-serve.c ejudge/super-serve.c
--- ejudge-2.3.18/super-serve.c	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/super-serve.c	2011-07-11 09:02:04.418275697 +0400
@@ -1,5 +1,5 @@
 /* -*- mode: c -*- */
-/* $Id: super-serve.c 6214 2011-04-01 20:03:14Z cher $ */
+/* $Id: super-serve.c 6359 2011-06-10 20:04:28Z cher $ */
 
 /* Copyright (C) 2003-2011 Alexander Chernov <cher@ejudge.ru> */
 
@@ -466,7 +466,7 @@ prepare_run_serving(const struct contest
     return;
   }
 
-  snprintf(run_log_path, sizeof(run_log_path), "%s/run_messages",
+  snprintf(run_log_path, sizeof(run_log_path), "%s/ej-run-messages.log",
            extra->var_dir);
 
   if ((queue_dir_fd = open(run_queue_dir, O_RDONLY, 0)) < 0) {
@@ -1627,6 +1627,8 @@ sid_state_clear(struct sid_state *p)
   super_serve_clear_edited_contest(p);
   xfree(p->user_login);
   xfree(p->user_name);
+  xfree(p->user_filter);
+  bitset_free(&p->marked);
   XMEMZERO(p, 1);
 }
 static struct sid_state*
@@ -2640,6 +2642,8 @@ cmd_simple_top_command(struct client_sta
   case SSERV_CMD_CNTS_CLEAR_TEAM_URL:
   case SSERV_CMD_CNTS_CLEAR_STANDINGS_URL:
   case SSERV_CMD_CNTS_CLEAR_PROBLEMS_URL:
+  case SSERV_CMD_CNTS_CLEAR_LOGO_URL:
+  case SSERV_CMD_CNTS_CLEAR_CSS_URL:
   case SSERV_CMD_CNTS_CLEAR_ROOT_DIR:
   case SSERV_CMD_CNTS_CLEAR_CONF_DIR:
   case SSERV_CMD_CNTS_CLEAR_DIR_MODE:
@@ -2774,6 +2778,8 @@ cmd_set_value(struct client_state *p, in
   case SSERV_CMD_CNTS_CHANGE_TEAM_URL:
   case SSERV_CMD_CNTS_CHANGE_STANDINGS_URL:
   case SSERV_CMD_CNTS_CHANGE_PROBLEMS_URL:
+  case SSERV_CMD_CNTS_CHANGE_LOGO_URL:
+  case SSERV_CMD_CNTS_CHANGE_CSS_URL:
   case SSERV_CMD_CNTS_CHANGE_ROOT_DIR:
   case SSERV_CMD_CNTS_CHANGE_CONF_DIR:
   case SSERV_CMD_CNTS_CHANGE_DIR_MODE:
@@ -3483,6 +3489,7 @@ cmd_http_request(
   hr.ip = p->ip;
   hr.ssl_flag = p->ssl;
   hr.system_login = userlist_login;
+  hr.userlist_clnt = userlist_clnt;
 
   hr.ss = sid_state_get(p->cookie, p->user_id, p->login, p->name);
   hr.config = config;
@@ -3633,6 +3640,8 @@ static const struct packet_handler packe
   [SSERV_CMD_CNTS_CLEAR_TEAM_URL] = { cmd_simple_top_command },
   [SSERV_CMD_CNTS_CLEAR_STANDINGS_URL] = { cmd_simple_top_command },
   [SSERV_CMD_CNTS_CLEAR_PROBLEMS_URL] = { cmd_simple_top_command },
+  [SSERV_CMD_CNTS_CLEAR_LOGO_URL] = { cmd_simple_top_command },
+  [SSERV_CMD_CNTS_CLEAR_CSS_URL] = { cmd_simple_top_command },
   [SSERV_CMD_CNTS_CLEAR_ROOT_DIR] = { cmd_simple_top_command },
   [SSERV_CMD_CNTS_CLEAR_CONF_DIR] = { cmd_simple_top_command },
   [SSERV_CMD_CNTS_CLEAR_DIR_MODE] = { cmd_simple_top_command },
@@ -3708,6 +3717,8 @@ static const struct packet_handler packe
   [SSERV_CMD_CNTS_CHANGE_TEAM_URL] = { cmd_set_value },
   [SSERV_CMD_CNTS_CHANGE_STANDINGS_URL] = { cmd_set_value },
   [SSERV_CMD_CNTS_CHANGE_PROBLEMS_URL] = { cmd_set_value },
+  [SSERV_CMD_CNTS_CHANGE_LOGO_URL] = { cmd_set_value },
+  [SSERV_CMD_CNTS_CHANGE_CSS_URL] = { cmd_set_value },
   [SSERV_CMD_CNTS_CHANGE_ROOT_DIR] = { cmd_set_value },
   [SSERV_CMD_CNTS_CHANGE_CONF_DIR] = { cmd_set_value },
   [SSERV_CMD_CNTS_CHANGE_DIR_MODE] = { cmd_set_value },
@@ -3883,6 +3894,8 @@ static const struct packet_handler packe
   [SSERV_CMD_PROB_CHANGE_MAX_STACK_SIZE] = { cmd_set_value },
   [SSERV_CMD_PROB_CHANGE_MAX_CORE_SIZE] = { cmd_set_value },
   [SSERV_CMD_PROB_CHANGE_MAX_FILE_SIZE] = { cmd_set_value },
+  [SSERV_CMD_PROB_CHANGE_MAX_OPEN_FILE_COUNT] = { cmd_set_value },
+  [SSERV_CMD_PROB_CHANGE_MAX_PROCESS_COUNT] = { cmd_set_value },
   [SSERV_CMD_PROB_CHANGE_INPUT_FILE] = { cmd_set_value },
   [SSERV_CMD_PROB_CLEAR_INPUT_FILE] = { cmd_set_value },
   [SSERV_CMD_PROB_CHANGE_OUTPUT_FILE] = { cmd_set_value },
@@ -4263,7 +4276,7 @@ contest_mngmt_cmd(const struct contest_d
     snprintf(log_path, sizeof(log_path), "%s/var/messages", cnts->root_dir);
     goto do_truncate_log;
   case SSERV_CMD_RUN_LOG_TRUNC:
-    snprintf(log_path, sizeof(log_path), "%s/var/run_messages", cnts->root_dir);
+    snprintf(log_path, sizeof(log_path), "%s/var/ej-run-messages.log", cnts->root_dir);
     goto do_truncate_log;
 
   do_truncate_log:
@@ -4277,7 +4290,7 @@ contest_mngmt_cmd(const struct contest_d
     snprintf(log_path, sizeof(log_path), "%s/var/messages", cnts->root_dir);
     goto do_dev_null;
   case SSERV_CMD_RUN_LOG_DEV_NULL:
-    snprintf(log_path, sizeof(log_path), "%s/var/run_messages", cnts->root_dir);
+    snprintf(log_path, sizeof(log_path), "%s/var/ej-run-messages.log", cnts->root_dir);
     goto do_dev_null;
 
   do_dev_null:
@@ -4295,7 +4308,7 @@ contest_mngmt_cmd(const struct contest_d
     snprintf(log_path, sizeof(log_path), "%s/var/messages", cnts->root_dir);
     goto do_file;
   case SSERV_CMD_RUN_LOG_FILE:
-    snprintf(log_path, sizeof(log_path), "%s/var/run_messages", cnts->root_dir);
+    snprintf(log_path, sizeof(log_path), "%s/var/ej-run-messages.log", cnts->root_dir);
     goto do_file;
 
   do_file:
diff -ruNp ejudge-2.3.18/super-serve.h ejudge/super-serve.h
--- ejudge-2.3.18/super-serve.h	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/super-serve.h	2011-07-11 09:02:04.429275495 +0400
@@ -1,9 +1,9 @@
 /* -*- c -*- */
-/* $Id: super-serve.h 5932 2010-07-07 09:00:22Z cher $ */
+/* $Id: super-serve.h 6359 2011-06-10 20:04:28Z cher $ */
 #ifndef __SUPER_SERVE_H__
 #define __SUPER_SERVE_H__
 
-/* Copyright (C) 2004-2010 Alexander Chernov <cher@ejudge.ru> */
+/* Copyright (C) 2004-2011 Alexander Chernov <cher@ejudge.ru> */
 
 /*
  * This program is free software; you can redistribute it and/or modify
@@ -20,6 +20,7 @@
 #include "ej_types.h"
 
 #include "opcaps.h"
+#include "bitset.h"
 
 #include <time.h>
 
@@ -83,7 +84,7 @@ struct section_language_data;
 struct section_problem_data;
 struct section_tester_data;
 
-/* sizeof(struct sid_state) == 416 */
+/* sizeof(struct sid_state) == 480 */
 struct sid_state
 {
   struct sid_state *next;
@@ -207,9 +208,32 @@ struct sid_state
   unsigned char *var_footer_text;
 
   unsigned char *compile_home_dir;
+
+  ejintbool_t user_filter_set;
+  unsigned char *user_filter;
+  int user_offset;
+  int user_count;
+
+  ejintbool_t group_filter_set;
+  unsigned char *group_filter;
+  int group_offset;
+  int group_count;
+
+  ejintbool_t contest_user_filter_set;
+  unsigned char *contest_user_filter;
+  int contest_user_offset;
+  int contest_user_count;
+
+  ejintbool_t group_user_filter_set;
+  unsigned char *group_user_filter;
+  int group_user_offset;
+  int group_user_count;
+
+  bitset_t marked;
 };
 
 struct sid_state;
+struct userlist_conn;
 struct super_http_request_info
 {
   // program invocation arguments
@@ -226,6 +250,7 @@ struct super_http_request_info
 
   const struct ejudge_cfg *config;
   struct sid_state *ss;
+  struct userlist_clnt *userlist_clnt;
 
   int opcode;
 
diff -ruNp ejudge-2.3.18/super-serve_meta.c ejudge/super-serve_meta.c
--- ejudge-2.3.18/super-serve_meta.c	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/super-serve_meta.c	2011-07-11 09:02:04.578272755 +0400
@@ -1,5 +1,5 @@
 // This is an auto-generated file, do not edit
-// Generated 2011/03/26 13:51:20
+// Generated 2011/06/11 00:04:30
 
 #include "super-serve_meta.h"
 #include "super-serve.h"
@@ -116,6 +116,23 @@ static struct meta_info_item meta_info_s
   [SSSS_var_header_text] = { SSSS_var_header_text, 's', XSIZE(struct sid_state, var_header_text), "var_header_text", XOFFSET(struct sid_state, var_header_text) },
   [SSSS_var_footer_text] = { SSSS_var_footer_text, 's', XSIZE(struct sid_state, var_footer_text), "var_footer_text", XOFFSET(struct sid_state, var_footer_text) },
   [SSSS_compile_home_dir] = { SSSS_compile_home_dir, 's', XSIZE(struct sid_state, compile_home_dir), "compile_home_dir", XOFFSET(struct sid_state, compile_home_dir) },
+  [SSSS_user_filter_set] = { SSSS_user_filter_set, 'B', XSIZE(struct sid_state, user_filter_set), "user_filter_set", XOFFSET(struct sid_state, user_filter_set) },
+  [SSSS_user_filter] = { SSSS_user_filter, 's', XSIZE(struct sid_state, user_filter), "user_filter", XOFFSET(struct sid_state, user_filter) },
+  [SSSS_user_offset] = { SSSS_user_offset, 'i', XSIZE(struct sid_state, user_offset), "user_offset", XOFFSET(struct sid_state, user_offset) },
+  [SSSS_user_count] = { SSSS_user_count, 'i', XSIZE(struct sid_state, user_count), "user_count", XOFFSET(struct sid_state, user_count) },
+  [SSSS_group_filter_set] = { SSSS_group_filter_set, 'B', XSIZE(struct sid_state, group_filter_set), "group_filter_set", XOFFSET(struct sid_state, group_filter_set) },
+  [SSSS_group_filter] = { SSSS_group_filter, 's', XSIZE(struct sid_state, group_filter), "group_filter", XOFFSET(struct sid_state, group_filter) },
+  [SSSS_group_offset] = { SSSS_group_offset, 'i', XSIZE(struct sid_state, group_offset), "group_offset", XOFFSET(struct sid_state, group_offset) },
+  [SSSS_group_count] = { SSSS_group_count, 'i', XSIZE(struct sid_state, group_count), "group_count", XOFFSET(struct sid_state, group_count) },
+  [SSSS_contest_user_filter_set] = { SSSS_contest_user_filter_set, 'B', XSIZE(struct sid_state, contest_user_filter_set), "contest_user_filter_set", XOFFSET(struct sid_state, contest_user_filter_set) },
+  [SSSS_contest_user_filter] = { SSSS_contest_user_filter, 's', XSIZE(struct sid_state, contest_user_filter), "contest_user_filter", XOFFSET(struct sid_state, contest_user_filter) },
+  [SSSS_contest_user_offset] = { SSSS_contest_user_offset, 'i', XSIZE(struct sid_state, contest_user_offset), "contest_user_offset", XOFFSET(struct sid_state, contest_user_offset) },
+  [SSSS_contest_user_count] = { SSSS_contest_user_count, 'i', XSIZE(struct sid_state, contest_user_count), "contest_user_count", XOFFSET(struct sid_state, contest_user_count) },
+  [SSSS_group_user_filter_set] = { SSSS_group_user_filter_set, 'B', XSIZE(struct sid_state, group_user_filter_set), "group_user_filter_set", XOFFSET(struct sid_state, group_user_filter_set) },
+  [SSSS_group_user_filter] = { SSSS_group_user_filter, 's', XSIZE(struct sid_state, group_user_filter), "group_user_filter", XOFFSET(struct sid_state, group_user_filter) },
+  [SSSS_group_user_offset] = { SSSS_group_user_offset, 'i', XSIZE(struct sid_state, group_user_offset), "group_user_offset", XOFFSET(struct sid_state, group_user_offset) },
+  [SSSS_group_user_count] = { SSSS_group_user_count, 'i', XSIZE(struct sid_state, group_user_count), "group_user_count", XOFFSET(struct sid_state, group_user_count) },
+  [SSSS_marked] = { SSSS_marked, '?', XSIZE(struct sid_state, marked), "marked", XOFFSET(struct sid_state, marked) },
 };
 
 int ss_sid_state_get_type(int tag)
@@ -158,6 +175,8 @@ int ss_sid_state_lookup_field(const char
 
 const struct meta_methods ss_sid_state_methods =
 {
+  SSSS_LAST_FIELD,
+  sizeof(struct sid_state),
   ss_sid_state_get_type,
   ss_sid_state_get_size,
   ss_sid_state_get_name,
diff -ruNp ejudge-2.3.18/super-serve_meta.h ejudge/super-serve_meta.h
--- ejudge-2.3.18/super-serve_meta.h	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/super-serve_meta.h	2011-07-11 09:02:04.587272589 +0400
@@ -1,5 +1,5 @@
 // This is an auto-generated file, do not edit
-// Generated 2011/03/26 13:51:20
+// Generated 2011/06/11 00:04:30
 
 #ifndef __SUPER_SERVE_META_H__
 #define __SUPER_SERVE_META_H__
@@ -111,6 +111,23 @@ enum
   SSSS_var_header_text,
   SSSS_var_footer_text,
   SSSS_compile_home_dir,
+  SSSS_user_filter_set,
+  SSSS_user_filter,
+  SSSS_user_offset,
+  SSSS_user_count,
+  SSSS_group_filter_set,
+  SSSS_group_filter,
+  SSSS_group_offset,
+  SSSS_group_count,
+  SSSS_contest_user_filter_set,
+  SSSS_contest_user_filter,
+  SSSS_contest_user_offset,
+  SSSS_contest_user_count,
+  SSSS_group_user_filter_set,
+  SSSS_group_user_filter,
+  SSSS_group_user_offset,
+  SSSS_group_user_count,
+  SSSS_marked,
 
   SSSS_LAST_FIELD,
 };
diff -ruNp ejudge-2.3.18/uldb_plugin.h ejudge/uldb_plugin.h
--- ejudge-2.3.18/uldb_plugin.h	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/uldb_plugin.h	2011-07-11 09:02:04.690270697 +0400
@@ -1,10 +1,10 @@
 /* -*- c -*- */
-/* $Id: uldb_plugin.h 5810 2010-05-28 14:48:51Z cher $ */
+/* $Id: uldb_plugin.h 6314 2011-05-08 07:01:11Z cher $ */
 
 #ifndef __ULDB_PLUGIN_H__
 #define __ULDB_PLUGIN_H__
 
-/* Copyright (C) 2006-2010 Alexander Chernov <cher@ejudge.ru> */
+/* Copyright (C) 2006-2011 Alexander Chernov <cher@ejudge.ru> */
 
 /*
  * This program is free software; you can redistribute it and/or modify
@@ -74,10 +74,20 @@ struct uldb_plugin_iface
   // get login by the user_id, login allocated on heap
   unsigned char *(*get_login)(void *, int);
   // create a new user
-  int (*new_user)(void *, const unsigned char *login,
+  int (*new_user)(void *,
+                  const unsigned char *login,
                   const unsigned char *email,
+                  int passwd_method,
                   const unsigned char *reg_passwd,
-                  int simple_reg_flag);
+                  int is_privileged,
+                  int is_invisible,
+                  int is_banned,
+                  int is_locked,
+                  int show_login,
+                  int show_email,
+                  int read_only,
+                  int never_clean,
+                  int simple_registration);
   // remove a user
   int (*remove_user)(void *, int);
   // find a cookie
@@ -133,7 +143,7 @@ struct uldb_plugin_iface
   // set the team password
   int (*set_team_passwd)(void *, int, int, int, const unsigned char *, time_t, int *);
   // register a user for contest
-  int (*register_contest)(void *, int, int, int, time_t, const struct userlist_contest**);
+  int (*register_contest)(void *, int, int, int, int, time_t, const struct userlist_contest**);
   // remove a particular member from a user
   int (*remove_member)(void *, int, int, int, time_t, int *);
   // check if the user is read-only
@@ -226,6 +236,18 @@ struct uldb_plugin_iface
   int (*create_group_member)(void *, int group_id, int user_id);
   // remove a group member
   int (*remove_group_member)(void *, int group_id, int user_id);
+  // list users
+  ptr_iterator_t (*get_brief_list_iterator_2)(void *, int contest_id, int group_id, const unsigned char *filter, int offset, int count);
+  // get the total count of users for the given filter
+  int (*get_user_count)(void *, int contest_id, int group_id, const unsigned char *filter, long long *p_count);
+  // get the group iterator
+  ptr_iterator_t (*get_group_iterator_2)(void *, const unsigned char *filter, int offset, int count);
+  // get the total number of groups to display
+  int (*get_group_count)(void *, const unsigned char *filter, long long *p_count);
+  // get the previous user
+  int (*get_prev_user_id)(void *, int contest_id, int group_id, int user_id, const unsigned char *filter, int *p_user_id);
+  // get the next user
+  int (*get_next_user_id)(void *, int contest_id, int group_id, int user_id, const unsigned char *filter, int *p_user_id);
 };
 
 /* default plugin: compiled into userlist-server */
diff -ruNp ejudge-2.3.18/uldb_plugin_xml.c ejudge/uldb_plugin_xml.c
--- ejudge-2.3.18/uldb_plugin_xml.c	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/uldb_plugin_xml.c	2011-07-11 09:02:04.427275531 +0400
@@ -1,5 +1,5 @@
 /* -*- mode: c -*- */
-/* $Id: uldb_plugin_xml.c 6162 2011-03-27 07:07:27Z cher $ */
+/* $Id: uldb_plugin_xml.c 6379 2011-06-24 18:47:55Z cher $ */
 
 /* Copyright (C) 2006-2011 Alexander Chernov <cher@ejudge.ru> */
 
@@ -58,8 +58,17 @@ static void forced_sync_func(void *);
 static unsigned char *get_login_func(void *, int);
 static int new_user_func(void *, const unsigned char *login,
                          const unsigned char *email,
+                         int passwd_method,
                          const unsigned char *reg_passwd,
-                         int simple_reg_flag);
+                         int is_privileged,
+                         int is_invisible,
+                         int is_banned,
+                         int is_locked,
+                         int show_login,
+                         int show_email,
+                         int read_only,
+                         int never_clean,
+                         int simple_registration);
 static int remove_user_func(void *, int);
 static int get_cookie_func(void *, ej_cookie_t,
                            const struct userlist_cookie **);
@@ -93,7 +102,7 @@ static ptr_iterator_t get_standings_list
 static int check_user_func(void *, int);
 static int set_reg_passwd_func(void *, int, int, const unsigned char *, time_t);
 static int set_team_passwd_func(void *, int, int, int, const unsigned char *, time_t, int *);
-static int register_contest_func(void *, int, int, int, time_t, const struct userlist_contest **);
+static int register_contest_func(void *, int, int, int, int, time_t, const struct userlist_contest **);
 static int remove_member_func(void *, int, int, int, time_t, int *);
 static int is_read_only_func(void *, int, int);
 static ptr_iterator_t get_info_list_iterator_func(void *, int, unsigned int);
@@ -175,6 +184,48 @@ static int
 create_group_member_func(void *data, int group_id, int user_id);
 static int
 remove_group_member_func(void *data, int group_id, int user_id);
+static ptr_iterator_t
+get_brief_list_iterator_2_func(
+        void *data,
+        int contest_id,
+        int group_id,
+        const unsigned char *filter,
+        int offset,
+        int count);
+static int
+get_user_count_func(
+        void *data,
+        int contest_id,
+        int group_id,
+        const unsigned char *filter,
+        long long *p_count);
+static ptr_iterator_t
+get_group_iterator_2_func(
+        void *data,
+        const unsigned char *filter,
+        int offset,
+        int count);
+static int
+get_group_count_func(
+        void *data,
+        const unsigned char *filter,
+        long long *p_count);
+static int
+get_prev_user_id_func(
+        void *data,
+        int contest_id,
+        int group_id,
+        int user_id,
+        const unsigned char *filter,
+        int *p_user_id);
+static int
+get_next_user_id_func(
+        void *data,
+        int contest_id,
+        int group_id,
+        int user_id,
+        const unsigned char *filter,
+        int *p_user_id);
 
 struct uldb_plugin_iface uldb_plugin_xml =
 {
@@ -276,6 +327,12 @@ struct uldb_plugin_iface uldb_plugin_xml
   get_group_member_iterator_func,
   create_group_member_func,
   remove_group_member_func,
+  get_brief_list_iterator_2_func,
+  get_user_count_func,
+  get_group_iterator_2_func,
+  get_group_count_func,
+  get_prev_user_id_func,
+  get_next_user_id_func,
 };
 
 struct uldb_xml_state
@@ -330,6 +387,18 @@ struct info_list_iterator
   int user_id;
 };
 
+struct brief_list_2_iterator
+{
+  struct ptr_iterator b;
+  struct uldb_xml_state *state;
+  int contest_id;
+  int group_id;
+  unsigned char *filter;
+  int offset;
+  int count;
+  int user_id;
+};
+
 static struct userlist_user_info *
 userlist_clone_user_info(
         struct userlist_user *u,
@@ -650,11 +719,21 @@ get_login_func(void *data, int user_id)
 }
 
 static int
-new_user_func(void *data,
-              const unsigned char *login,
-              const unsigned char *email,
-              const unsigned char *reg_passwd,
-              int simple_reg_flag)
+new_user_func(
+        void *data,
+        const unsigned char *login,
+        const unsigned char *email,
+        int passwd_method,
+        const unsigned char *reg_passwd,
+        int is_privileged,
+        int is_invisible,
+        int is_banned,
+        int is_locked,
+        int show_login,
+        int show_email,
+        int read_only,
+        int never_clean,
+        int simple_registration)
 {
   struct uldb_xml_state *state = (struct uldb_xml_state*) data;
   struct userlist_list *ul = state->userlist;
@@ -690,11 +769,19 @@ new_user_func(void *data,
   u->login = xstrdup(login);
   if (email) u->email = xstrdup(email);
   u->login_hash = userlist_login_hash(login);
-  u->simple_registration = simple_reg_flag;
+  u->is_privileged = is_privileged;
+  u->is_invisible = is_invisible;
+  u->is_banned = is_banned;
+  u->is_locked = is_locked;
+  u->show_login = show_login;
+  u->show_email = show_email;
+  u->read_only = read_only;
+  u->never_clean = never_clean;
+  u->simple_registration = simple_registration;
 
   if (reg_passwd) {
     u->passwd = xstrdup(reg_passwd);
-    u->passwd_method = USERLIST_PWD_PLAIN;
+    u->passwd_method = passwd_method;
   }
 
   if (ul->login_hash_table) {
@@ -1424,6 +1511,7 @@ register_contest_func(void *data,
                       int user_id,
                       int contest_id,
                       int status,
+                      int flags,
                       time_t cur_time,
                       const struct userlist_contest **p_c)
 {
@@ -1455,6 +1543,7 @@ register_contest_func(void *data,
   xml_link_node_last(u->contests, &c->b);
   c->id = contest_id;
   c->status = status;
+  c->flags = flags;
   c->create_time = cur_time;
 
   state->dirty = 1;
@@ -1748,6 +1837,7 @@ set_reg_flags_func(void *data, int user_
   case 1: new_value |= value; break;
   case 2: new_value &= ~value; break;
   case 3: new_value ^= value; break;
+  case 4: new_value = value; break;
   }
   if (new_value == c->flags) return 0;
 
@@ -3742,6 +3832,357 @@ remove_group_member_func(void *data, int
   return 0;
 }
 
+static void
+brief_list_2_do_skip(
+        struct brief_list_2_iterator *iter,
+        const struct userlist_list *ul)
+{
+  const struct userlist_user *u;
+  const struct xml_tree *t;
+  const struct userlist_contest *c;
+  const struct userlist_groupmember *gm;
+
+  for (;; ++iter->user_id) {
+    if (iter->user_id >= ul->user_map_size) return;
+    if (!(u = ul->user_map[iter->user_id])) continue;
+    if (iter->contest_id > 0 && iter->group_id > 0) {
+      if (!u->group_first) continue;
+      for (gm = (const struct userlist_groupmember*) u->group_first;
+           gm && gm->group_id < iter->group_id;
+           gm = (struct userlist_groupmember*) gm->group_next) {
+      }
+      if (!gm || gm->group_id != iter->group_id) continue;
+      if (!u->contests) continue;
+      for (t = u->contests->first_down; t; t = t->right) {
+        c = (const struct userlist_contest*) t;
+        if (c->id == iter->contest_id) {
+          if (iter->offset <= 0) return;
+          --iter->offset;
+          break;
+        }
+      }
+    } else if (iter->group_id > 0) {
+      if (!u->group_first) continue;
+      for (gm = (const struct userlist_groupmember*) u->group_first;
+           gm && gm->group_id < iter->group_id;
+           gm = (struct userlist_groupmember*) gm->group_next) {
+      }
+      if (!gm || gm->group_id != iter->group_id) continue;
+      if (iter->offset <= 0) return;
+      --iter->offset;
+    } else if (iter->contest_id > 0) {
+      if (!u->contests) continue;
+      for (t = u->contests->first_down; t; t = t->right) {
+        c = (const struct userlist_contest*) t;
+        if (c->id == iter->contest_id) {
+          if (iter->offset <= 0) return;
+          --iter->offset;
+          break;
+        }
+      }
+    } else {
+      if (iter->offset <= 0) return;
+      --iter->offset;
+      continue;
+    }
+  }
+}
+
+static int
+brief_list_2_iterator_has_next_func(ptr_iterator_t data)
+{
+  struct brief_list_2_iterator *iter = (struct brief_list_2_iterator*) data;
+  struct userlist_list *ul = iter->state->userlist;
+
+  if (iter->count <= 0) return 0;
+  brief_list_2_do_skip(iter, ul);
+  return (iter->user_id < ul->user_map_size);
+}
+static const void *
+brief_list_2_iterator_get_func(ptr_iterator_t data)
+{
+  struct brief_list_2_iterator *iter = (struct brief_list_2_iterator*) data;
+  struct userlist_list *ul = iter->state->userlist;
+
+  if (iter->count <= 0) return 0;
+  brief_list_2_do_skip(iter, ul);
+  if (iter->user_id >= ul->user_map_size) return 0;
+  return (const void *) ul->user_map[iter->user_id];
+}
+static void
+brief_list_2_iterator_next_func(ptr_iterator_t data)
+{
+  struct brief_list_2_iterator *iter = (struct brief_list_2_iterator*) data;
+  struct userlist_list *ul = iter->state->userlist;
+
+  if (iter->count <= 0) return;
+  if (iter->user_id < ul->user_map_size) iter->user_id++;
+  --iter->count;
+  brief_list_2_do_skip(iter, ul);
+}
+
+static void
+brief_list_2_iterator_destroy_func(ptr_iterator_t data)
+{
+  struct brief_list_2_iterator *iter = (struct brief_list_2_iterator*) data;
+  xfree(iter->filter);
+  xfree(iter);
+}
+
+static struct ptr_iterator brief_list_2_iterator_funcs =
+{
+  brief_list_2_iterator_has_next_func,
+  brief_list_2_iterator_get_func,
+  brief_list_2_iterator_next_func,
+  brief_list_2_iterator_destroy_func,
+};
+
+static ptr_iterator_t
+get_brief_list_iterator_2_func(
+        void *data,
+        int contest_id,
+        int group_id,
+        const unsigned char *filter,
+        int offset,
+        int count)
+{
+  struct uldb_xml_state *state = (struct uldb_xml_state*) data;
+  struct brief_list_2_iterator *iter;
+
+  if (offset < 0) offset = 0;
+  if (count < 0) count = 0;
+  if (offset + count < 0) count = 0;
+
+  XCALLOC(iter, 1);
+  iter->b = brief_list_2_iterator_funcs;
+  iter->state = state;
+  iter->contest_id = contest_id;
+  iter->group_id = group_id;
+  if (filter) iter->filter = xstrdup(filter);
+  iter->offset = offset;
+  iter->count = count;
+  iter->user_id = 0;
+  return (ptr_iterator_t) iter;
+}
+
+static int
+get_user_count_func(
+        void *data,
+        int contest_id,
+        int group_id,
+        const unsigned char *filter,
+        long long *p_count)
+{
+  struct uldb_xml_state *state = (struct uldb_xml_state*) data;
+  int i;
+  long long count = 0;
+
+  for (i = 0; i < state->userlist->user_map_size; ++i) {
+    if (state->userlist->user_map[i]) {
+      ++count;
+    }
+  }
+
+  if (p_count) *p_count = count;
+  return 0;
+}
+
+struct group_iterator_2
+{
+  struct ptr_iterator b;
+
+  struct uldb_xml_state *state;
+  unsigned char *filter;
+  int offset;
+  int count;
+
+  int group_id;
+};
+
+static int
+group_iterator_2_has_next_func(ptr_iterator_t data)
+{
+  struct group_iterator_2 *iter = (struct group_iterator_2 *) data;
+  struct userlist_list *ul;
+
+  if (!iter->state || !(ul = iter->state->userlist)) return 0;
+  if (ul->group_map_size <= 0 || iter->count <= 0) return 0;
+  return iter->group_id < ul->group_map_size;
+}
+static const void *
+group_iterator_2_get_func(ptr_iterator_t data)
+{
+  struct group_iterator_2 *iter = (struct group_iterator_2 *) data;
+  struct userlist_list *ul;
+
+  if (!iter->state || !(ul = iter->state->userlist)) return 0;
+  if (ul->group_map_size <= 0 || iter->count <= 0) return 0;
+  if (iter->group_id >= ul->group_map_size) return NULL;
+  return ul->group_map[iter->group_id];
+}
+static void
+group_iterator_2_next_func(ptr_iterator_t data)
+{
+  struct group_iterator_2 *iter = (struct group_iterator_2 *) data;
+  struct userlist_list *ul;
+
+  if (!iter->state || !(ul = iter->state->userlist)) return;
+  if (ul->group_map_size <= 0 || iter->count <= 0) return;
+  if (iter->group_id >= ul->group_map_size) return;
+  --iter->count; ++iter->group_id;
+  while (iter->group_id < ul->group_map_size && !ul->group_map[iter->group_id]) {
+    ++iter->group_id;
+  }
+}
+static void
+group_iterator_2_destroy_func(ptr_iterator_t data)
+{
+  struct group_iterator_2 *iter = (struct group_iterator_2 *) data;
+  xfree(iter->filter);
+  xfree(iter);
+}
+
+static struct ptr_iterator group_iterator_2_funcs =
+{
+  group_iterator_2_has_next_func,
+  group_iterator_2_get_func,
+  group_iterator_2_next_func,
+  group_iterator_2_destroy_func,
+};
+
+static ptr_iterator_t
+get_group_iterator_2_func(
+        void *data,
+        const unsigned char *filter,
+        int offset,
+        int count)
+{
+  struct uldb_xml_state *state = (struct uldb_xml_state*) data;
+  struct group_iterator_2 *iter = 0;
+  const struct userlist_list *ul = state->userlist;
+
+  if (offset < 0) offset = 0;
+  if (count < 0) count = 0;
+
+  XCALLOC(iter, 1);
+  iter->b = group_iterator_2_funcs;
+  iter->state = state;
+  iter->filter = xstrdup(filter);
+  iter->offset = offset;
+  iter->count = count;
+  iter->group_id = 0;
+
+  if (ul->group_map_size > 0 || iter->count > 0) {
+    while (iter->group_id < ul->group_map_size && !ul->group_map[iter->group_id]) {
+      ++iter->group_id;
+    }
+    while (iter->offset > 0 && iter->group_id < ul->group_map_size && ul->group_map[iter->group_id]) {
+      --iter->offset;
+      ++iter->group_id;
+      while (iter->group_id < ul->group_map_size && !ul->group_map[iter->group_id]) {
+        ++iter->group_id;
+      }
+    }
+  }
+
+  return (ptr_iterator_t) iter;
+}
+
+static int
+get_group_count_func(
+        void *data,
+        const unsigned char *filter,
+        long long *p_count)
+{
+  struct uldb_xml_state *state = (struct uldb_xml_state*) data;
+  int i;
+  long long count = 0;
+
+  for (i = 0; i < state->userlist->group_map_size; ++i) {
+    if (state->userlist->group_map[i]) {
+      ++count;
+    }
+  }
+
+  if (p_count) *p_count = count;
+  return 0;
+}
+
+static int
+check_user_match(const struct userlist_user *u, int contest_id, int group_id, const unsigned char *filter)
+{
+  const struct xml_tree *t;
+  const struct userlist_contest *c;
+  const struct userlist_groupmember *m;
+
+  if (!u) return 0;
+  if (contest_id > 0) {
+    if (!u->contests) return 0;
+    for (t = u->contests->first_down; t; t = t->right) {
+      c = (const struct userlist_contest*) t;
+      if (c->id == contest_id) {
+        break;
+      }
+    }
+    if (!t) return 0;
+  }
+  if (group_id > 0) {
+    if (!u->group_first) return 0;
+    for (t = u->group_first; t; t = m->group_next) {
+      m = (const struct userlist_groupmember*) t;
+      if (m->group_id == group_id) break;
+    }
+    if (!t) return 0;
+  }
+
+  return 1;
+}
+
+static int
+get_prev_user_id_func(
+        void *data,
+        int contest_id,
+        int group_id,
+        int user_id,
+        const unsigned char *filter,
+        int *p_user_id)
+{
+  struct uldb_xml_state *state = (struct uldb_xml_state*) data;
+  struct userlist_list *ul = state->userlist;
+
+  if (user_id > ul->user_map_size) user_id = ul->user_map_size;
+  for (--user_id;
+       user_id > 0 && !check_user_match(ul->user_map[user_id], contest_id, group_id, filter);
+       --user_id) {
+  }
+
+  if (p_user_id) *p_user_id = user_id;
+  return 0;
+}
+
+static int
+get_next_user_id_func(
+        void *data,
+        int contest_id,
+        int group_id,
+        int user_id,
+        const unsigned char *filter,
+        int *p_user_id)
+{
+  struct uldb_xml_state *state = (struct uldb_xml_state*) data;
+  struct userlist_list *ul = state->userlist;
+
+  if (user_id < 0) user_id = 0;
+  for (++user_id;
+       user_id < ul->user_map_size && !check_user_match(ul->user_map[user_id], contest_id, group_id, filter);
+       ++user_id) {
+  }
+  if (user_id >= ul->user_map_size) user_id = 0;
+
+  if (p_user_id) *p_user_id = user_id;
+  return 0;
+}
+
 /*
  * Local variables:
  *  compile-command: "make"
diff -ruNp ejudge-2.3.18/unix/ChangeLog ejudge/unix/ChangeLog
--- ejudge-2.3.18/unix/ChangeLog	2011-04-10 09:25:17.000000000 +0400
+++ ejudge/unix/ChangeLog	2011-07-11 09:01:59.716362041 +0400
@@ -1,3 +1,8 @@
+2011-06-18 (r6367) Alexander Chernov  <cher@ejudge.ru>
+
+	* reuse_exec.c:
+	(linux_set_secure_exec_supported_flag) check for 'dup' is removed.
+
 2011-03-31 (r6208) Alexander Chernov  <cher@ejudge.ru>
 
 	* reuse_exec.c:
@@ -718,5 +723,5 @@
 
 END
 
-	$Id: ChangeLog 6245 2011-04-10 05:24:07Z cher $
+	$Id: ChangeLog 6397 2011-07-06 05:01:19Z cher $
 
diff -ruNp ejudge-2.3.18/unix/reuse_exec.c ejudge/unix/reuse_exec.c
--- ejudge-2.3.18/unix/reuse_exec.c	2011-04-10 09:25:17.000000000 +0400
+++ ejudge/unix/reuse_exec.c	2011-07-11 09:01:59.733361729 +0400
@@ -1,4 +1,4 @@
-/* $Id: reuse_exec.c 6234 2011-04-08 17:43:28Z cher $ */
+/* $Id: reuse_exec.c 6367 2011-06-18 18:54:53Z cher $ */
 
 /* Copyright (C) 1998-2011 Alexander Chernov <cher@ejudge.ru> */
 /* Created: <1998-01-21 14:33:28 cher> */
@@ -2282,7 +2282,7 @@ linux_set_secure_exec_supported_flag(voi
 
   if (!pid) {
     if (ptrace(0x4281, 0, 0, 0) < 0) _exit(1);
-    if (dup(0) >= 0) _exit(1);
+    //if (dup(0) >= 0) _exit(1);
     _exit(0);
   }
 
diff -ruNp ejudge-2.3.18/userlist.c ejudge/userlist.c
--- ejudge-2.3.18/userlist.c	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/userlist.c	2011-07-11 09:02:04.188279925 +0400
@@ -1,5 +1,5 @@
 /* -*- mode: c -*- */
-/* $Id: userlist.c 6146 2011-03-26 10:47:14Z cher $ */
+/* $Id: userlist.c 6314 2011-05-08 07:01:11Z cher $ */
 
 /* Copyright (C) 2002-2011 Alexander Chernov <cher@ejudge.ru> */
 
@@ -1274,6 +1274,10 @@ static const int primes[] =
   262147,
   524309,
   1048583,
+  2097169,
+  4194319,
+  8388617,
+  16777259,
   0,
 };
 
@@ -1322,7 +1326,11 @@ userlist_build_login_hash(struct userlis
     p->login_hash_table[j] = u;
   }
   rdtscll(tsc2);
-  tsc2 = (tsc2 - tsc1) * 1000000 / cpu_frequency;
+  if (cpu_frequency > 0) {
+    tsc2 = (tsc2 - tsc1) * 1000000 / cpu_frequency;
+  } else {
+    tsc2 = tsc2 - tsc1;
+  }
 
   info("login hashtable: size = %" EJ_PRINTF_ZSPEC "u, shift = %" EJ_PRINTF_ZSPEC "u, thresh = %" EJ_PRINTF_ZSPEC "u, current = %" EJ_PRINTF_ZSPEC "u",
        EJ_PRINTF_ZCAST(p->login_hash_size), EJ_PRINTF_ZCAST(p->login_hash_step),
@@ -1409,7 +1417,11 @@ userlist_build_cookie_hash(struct userli
   }
 
   rdtscll(tsc2);
-  tsc2 = (tsc2 - tsc1) * 1000000 / cpu_frequency;
+  if (cpu_frequency > 0) {
+    tsc2 = (tsc2 - tsc1) * 1000000 / cpu_frequency;
+  } else {
+    tsc2 = tsc2 - tsc1;
+  }
 
   info("cookie hashtable: size = %" EJ_PRINTF_ZSPEC "u, step = %" EJ_PRINTF_ZSPEC "u, thresh = %" EJ_PRINTF_ZSPEC "u, current = %" EJ_PRINTF_ZSPEC "u",
        EJ_PRINTF_ZCAST(p->cookie_hash_size), EJ_PRINTF_ZCAST(p->cookie_hash_step),
diff -ruNp ejudge-2.3.18/userlist_clnt/ChangeLog ejudge/userlist_clnt/ChangeLog
--- ejudge-2.3.18/userlist_clnt/ChangeLog	2011-04-10 09:25:23.000000000 +0400
+++ ejudge/userlist_clnt/ChangeLog	2011-07-11 09:02:03.757287847 +0400
@@ -1,3 +1,31 @@
+2011-05-08 (r6315) Alexander Chernov  <cher@ejudge.ru>
+
+	* get_prev_user_id.c:
+	initial revision
+
+2011-05-01 (r6289) Alexander Chernov  <cher@ejudge.ru>
+
+	* create_user_2.c:
+	initial revision
+
+2011-04-22 (r6262) Alexander Chernov  <cher@ejudge.ru>
+
+	* get_count.c:
+	(userlist_clnt_get_count) 'group_id' parameter is added.
+
+	* list_users_2.c:
+	(userlist_clnt_list_users_2) 'group_id' parameter is added.
+
+2011-04-20 (r6254) Alexander Chernov  <cher@ejudge.ru>
+
+	* get_count.c:
+	initial revision
+
+2011-04-19 (r6249) Alexander Chernov  <cher@ejudge.ru>
+
+	* list_users_2.c:
+	initial revision
+
 2011-03-27 (r6169) Alexander Chernov  <cher@ejudge.ru>
 
 	* send_packet.c:
diff -ruNp ejudge-2.3.18/userlist_clnt/create_user_2.c ejudge/userlist_clnt/create_user_2.c
--- ejudge-2.3.18/userlist_clnt/create_user_2.c	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/userlist_clnt/create_user_2.c	2011-07-11 09:02:03.735288253 +0400
@@ -0,0 +1,137 @@
+/* -*- mode: c -*- */
+/* $Id: create_user_2.c 6296 2011-05-02 20:57:23Z cher $ */
+
+/* Copyright (C) 2011 Alexander Chernov <cher@ejudge.ru> */
+
+/*
+ * 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.
+ */
+
+#include "userlist_clnt/private.h"
+
+enum { MAX_LEN = 65535, MAX_SIZE = 1024*1024 };
+
+int
+userlist_clnt_create_user_2(
+        struct userlist_clnt *clnt,
+        int cmd,
+        const struct userlist_pk_create_user_2 *params,
+        const unsigned char *login_str,
+        const unsigned char *email_str,
+        const unsigned char *reg_password_str,
+        const unsigned char *cnts_password_str,
+        const unsigned char *cnts_name_str,
+        int *p_user_id)
+{
+  if (!params) return -ULS_ERR_PROTOCOL;
+  if (!login_str) login_str = "";
+  if (!email_str) email_str = "";
+  if (!reg_password_str) reg_password_str = "";
+  if (!cnts_password_str) cnts_password_str = "";
+  if (!cnts_name_str) cnts_name_str = "";
+
+  size_t login_len = strlen(login_str);
+  if (login_len > MAX_LEN) return -ULS_ERR_PROTOCOL;
+  size_t email_len = strlen(email_str);
+  if (email_len > MAX_LEN) return -ULS_ERR_PROTOCOL;
+  size_t reg_password_len = strlen(reg_password_str);
+  if (reg_password_len > MAX_LEN) return -ULS_ERR_PROTOCOL;
+  size_t cnts_password_len = strlen(cnts_password_str);
+  if (cnts_password_len > MAX_LEN) return -ULS_ERR_PROTOCOL;
+  size_t cnts_name_len = strlen(cnts_name_str);
+  if (cnts_name_len > MAX_LEN) return -ULS_ERR_PROTOCOL;
+
+  size_t out_size = sizeof(struct userlist_pk_create_user_2) + login_len + email_len + reg_password_len + cnts_password_len + cnts_name_len;
+  if (out_size > MAX_SIZE) return -ULS_ERR_PROTOCOL;
+
+  struct userlist_pk_create_user_2 *out = (struct userlist_pk_create_user_2*) xcalloc(out_size, 1);
+  unsigned char *login_ptr = out->data;
+  unsigned char *email_ptr = login_ptr + login_len + 1;
+  unsigned char *reg_password_ptr = email_ptr + email_len + 1;
+  unsigned char *cnts_password_ptr = reg_password_ptr + reg_password_len + 1;
+  unsigned char *cnts_name_ptr = cnts_password_ptr + cnts_password_len + 1;
+
+  out->request_id = cmd;
+  out->login_len = login_len;
+  out->email_len = email_len;
+  out->send_email_flag = params->send_email_flag;
+  out->confirm_email_flag = params->confirm_email_flag;
+  out->random_password_flag = params->random_password_flag;
+  out->reg_password_len = reg_password_len;
+  out->use_sha1_flag = params->use_sha1_flag;
+  out->is_privileged_flag = params->is_privileged_flag;
+  out->is_invisible_flag = params->is_invisible_flag;
+  out->is_banned_flag = params->is_banned_flag;
+  out->is_locked_flag = params->is_locked_flag;
+  out->show_login_flag = params->show_login_flag;
+  out->show_email_flag = params->show_email_flag;
+  out->read_only_flag = params->read_only_flag;
+  out->never_clean_flag = params->never_clean_flag;
+  out->simple_registration_flag = params->simple_registration_flag;
+  out->contest_id = params->contest_id;
+  out->cnts_status = params->cnts_status;
+  out->cnts_is_invisible_flag = params->cnts_is_invisible_flag;
+  out->cnts_is_banned_flag = params->cnts_is_banned_flag;
+  out->cnts_is_locked_flag = params->cnts_is_locked_flag;
+  out->cnts_is_incomplete_flag = params->cnts_is_incomplete_flag;
+  out->cnts_is_disqualified_flag = params->cnts_is_disqualified_flag;
+  out->cnts_use_reg_passwd_flag = params->cnts_use_reg_passwd_flag;
+  out->cnts_set_null_passwd_flag = params->cnts_set_null_passwd_flag;
+  out->cnts_random_password_flag = params->cnts_random_password_flag;
+  out->cnts_password_len = cnts_password_len;
+  out->cnts_use_sha1_flag = params->cnts_use_sha1_flag;
+  out->cnts_name_len = cnts_name_len;
+  out->group_id = params->group_id;
+
+  memcpy(login_ptr, login_str, login_len + 1);
+  memcpy(email_ptr, email_str, email_len + 1);
+  memcpy(reg_password_ptr, reg_password_str, reg_password_len + 1);
+  memcpy(cnts_password_ptr, cnts_password_str, cnts_password_len + 1);
+  memcpy(cnts_name_ptr, cnts_name_str, cnts_name_len + 1);
+
+  int r = 0;
+  if ((r = userlist_clnt_send_packet(clnt, out_size, out)) < 0) return r;
+
+  size_t in_size = 0;
+  void *void_in = 0;
+  if ((r = userlist_clnt_read_and_notify(clnt, &in_size, &void_in)) < 0)
+    return r;
+  struct userlist_packet *in = (struct userlist_packet *) void_in;
+  struct userlist_pk_login_ok *uin = 0;
+  if (in_size < sizeof(*in)) {
+    xfree(in);
+    return -ULS_ERR_PROTOCOL;
+  }
+  if (in->id == ULS_LOGIN_OK) {
+    uin = (struct userlist_pk_login_ok*) in;
+    if (in_size != sizeof(*uin)) {
+      xfree(in);
+      return -ULS_ERR_PROTOCOL;
+    }
+    if (p_user_id) *p_user_id = uin->user_id;
+    xfree(in);
+    return ULS_LOGIN_OK;
+  }
+
+  if (in_size != sizeof(*in)) {
+    xfree(in);
+    return -ULS_ERR_PROTOCOL;
+  }
+  r = in->id;
+  xfree(in);
+  return r;
+}
+
+/*
+ * Local variables:
+ *  compile-command: "make -C .."
+ * End:
+ */
diff -ruNp ejudge-2.3.18/userlist_clnt/get_count.c ejudge/userlist_clnt/get_count.c
--- ejudge-2.3.18/userlist_clnt/get_count.c	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/userlist_clnt/get_count.c	2011-07-11 09:02:03.783287370 +0400
@@ -0,0 +1,87 @@
+/* -*- mode: c -*- */
+/* $Id: get_count.c 6262 2011-04-22 05:15:27Z cher $ */
+
+/* Copyright (C) 2011 Alexander Chernov <cher@ejudge.ru> */
+
+/*
+ * 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.
+ */
+
+#include "userlist_clnt/private.h"
+
+enum { MAX_FILTER_LEN = 8192 };
+
+int
+userlist_clnt_get_count(
+        struct userlist_clnt *clnt,
+        int cmd,
+        int contest_id,
+        int group_id,
+        const unsigned char *filter,
+        long long *p_count)
+{
+  struct userlist_pk_list_users_2 *out = 0;
+  struct userlist_pk_count *in = 0;
+  struct userlist_packet *in_generic = 0;
+  void *in_void = 0;
+  int filter_len = 0, r;
+  size_t out_size, in_size = 0;
+  long long count = 0;
+
+  if (!filter) filter = "";
+  filter_len = strlen(filter);
+  if (filter_len < 0 || filter_len > MAX_FILTER_LEN)
+    return -ULS_ERR_PROTOCOL;
+
+  out_size = sizeof(*out) + filter_len;
+  out = alloca(out_size);
+  memset(out, 0, out_size);
+  out->request_id = cmd;
+  out->contest_id = contest_id;
+  out->group_id = group_id;
+  out->filter_len = filter_len;
+  memcpy(out->data, filter, filter_len + 1);
+
+  if ((r = userlist_clnt_send_packet(clnt, out_size, out)) < 0) return r;
+  if ((r = userlist_clnt_read_and_notify(clnt, &in_size, &in_void)) < 0)
+    return r;
+
+  if (in_size < sizeof(*in_generic)) {
+    xfree(in_void); in_void = 0;
+    return -ULS_ERR_PROTOCOL;
+  }
+  in_generic = (struct userlist_packet *) in_void; in_void = 0;
+  if (in_generic->id < 0) {
+    r = in_generic->id;
+    xfree(in_generic); in_generic = 0;
+    return r;
+  }
+  if (in_generic->id != ULS_COUNT) {
+    r = in_generic->id;
+    xfree(in_generic); in_generic = 0;
+    return -ULS_ERR_PROTOCOL;
+  }
+  if (in_size != sizeof(*in)) {
+    xfree(in_generic); in_generic = 0;
+    return -ULS_ERR_PROTOCOL;
+  }
+  in = (struct userlist_pk_count*) in_generic; in_generic = 0;
+  count = in->count;
+  if (count < 0) {
+    xfree(in); in = 0;
+    return -ULS_ERR_PROTOCOL;
+  }
+  xfree(in); in = 0;
+  if (p_count) *p_count = count;
+
+  return ULS_COUNT;
+}
+
diff -ruNp ejudge-2.3.18/userlist_clnt/get_prev_user_id.c ejudge/userlist_clnt/get_prev_user_id.c
--- ejudge-2.3.18/userlist_clnt/get_prev_user_id.c	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/userlist_clnt/get_prev_user_id.c	2011-07-11 09:02:03.740288159 +0400
@@ -0,0 +1,88 @@
+/* -*- mode: c -*- */
+/* $Id: get_prev_user_id.c 6321 2011-05-08 18:56:21Z cher $ */
+
+/* Copyright (C) 2011 Alexander Chernov <cher@ejudge.ru> */
+
+/*
+ * 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.
+ */
+
+#include "userlist_clnt/private.h"
+
+enum { MAX_FILTER_LEN = 8192 };
+
+int
+userlist_clnt_get_prev_user_id(
+        struct userlist_clnt *clnt,
+        int cmd,
+        int contest_id,
+        int group_id,
+        int user_id,
+        const unsigned char *filter,
+        int *p_user_id)
+{
+  struct userlist_pk_list_users_2 *out = 0;
+  struct userlist_pk_login_ok *in = 0;
+  struct userlist_packet *in_generic = 0;
+  void *in_void = 0;
+  int filter_len = 0, r;
+  size_t out_size, in_size = 0;
+
+  if (!filter) filter = "";
+  filter_len = strlen(filter);
+  if (filter_len < 0 || filter_len > MAX_FILTER_LEN)
+    return -ULS_ERR_PROTOCOL;
+
+  out_size = sizeof(*out) + filter_len;
+  out = alloca(out_size);
+  memset(out, 0, out_size);
+  out->request_id = cmd;
+  out->contest_id = contest_id;
+  out->group_id = group_id;
+  out->user_id = user_id;
+  out->filter_len = filter_len;
+  memcpy(out->data, filter, filter_len + 1);
+
+  if ((r = userlist_clnt_send_packet(clnt, out_size, out)) < 0) return r;
+  if ((r = userlist_clnt_read_and_notify(clnt, &in_size, &in_void)) < 0)
+    return r;
+
+  if (in_size < sizeof(*in_generic)) {
+    xfree(in_void); in_void = 0;
+    return -ULS_ERR_PROTOCOL;
+  }
+  in_generic = (struct userlist_packet *) in_void; in_void = 0;
+  if (in_generic->id < 0) {
+    r = in_generic->id;
+    xfree(in_generic); in_generic = 0;
+    return r;
+  }
+  if (in_generic->id != ULS_LOGIN_OK) {
+    r = in_generic->id;
+    xfree(in_generic); in_generic = 0;
+    return -ULS_ERR_PROTOCOL;
+  }
+  if (in_size != sizeof(*in)) {
+    xfree(in_generic); in_generic = 0;
+    return -ULS_ERR_PROTOCOL;
+  }
+  in = (struct userlist_pk_login_ok*) in_generic; in_generic = 0;
+  user_id = in->user_id;
+  if (user_id < 0) {
+    xfree(in); in = 0;
+    return -ULS_ERR_PROTOCOL;
+  }
+  xfree(in); in = 0;
+  if (p_user_id) *p_user_id = user_id;
+
+  return ULS_LOGIN_OK;
+}
+
diff -ruNp ejudge-2.3.18/userlist_clnt/list_users_2.c ejudge/userlist_clnt/list_users_2.c
--- ejudge-2.3.18/userlist_clnt/list_users_2.c	1970-01-01 03:00:00.000000000 +0300
+++ ejudge/userlist_clnt/list_users_2.c	2011-07-11 09:02:03.766287682 +0400
@@ -0,0 +1,94 @@
+/* -*- mode: c -*- */
+/* $Id: list_users_2.c 6262 2011-04-22 05:15:27Z cher $ */
+
+/* Copyright (C) 2011 Alexander Chernov <cher@ejudge.ru> */
+
+/*
+ * 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.
+ */
+
+#include "userlist_clnt/private.h"
+
+enum { MAX_FILTER_LEN = 8192 };
+enum { MAX_XML_LEN = 134217728 };
+
+int
+userlist_clnt_list_users_2(
+        struct userlist_clnt *clnt,
+        int cmd,
+        int contest_id,
+        int group_id,
+        const unsigned char *filter,
+        int offset,
+        int count,
+        unsigned char **p_info)
+{
+  struct userlist_pk_list_users_2 *out = 0;
+  struct userlist_pk_xml_data *in = 0;
+  struct userlist_packet *in_generic = 0;
+  void *in_void = 0;
+  int filter_len = 0, r;
+  size_t out_size, in_size = 0;
+
+  if (!filter) filter = "";
+  filter_len = strlen(filter);
+  if (filter_len < 0 || filter_len > MAX_FILTER_LEN)
+    return -ULS_ERR_PROTOCOL;
+
+  out_size = sizeof(*out) + filter_len;
+  out = alloca(out_size);
+  memset(out, 0, out_size);
+  out->request_id = cmd;
+  out->contest_id = contest_id;
+  out->group_id = group_id;
+  out->filter_len = filter_len;
+  out->offset = offset;
+  out->count = count;
+  memcpy(out->data, filter, filter_len + 1);
+
+  if ((r = userlist_clnt_send_packet(clnt, out_size, out)) < 0) return r;
+  if ((r = userlist_clnt_read_and_notify(clnt, &in_size, &in_void)) < 0)
+    return r;
+
+  if (in_size < sizeof(*in_generic)) {
+    xfree(in_void); in_void = 0;
+    return -ULS_ERR_PROTOCOL;
+  }
+  in_generic = (struct userlist_packet *) in_void; in_void = 0;
+  if (in_generic->id < 0) {
+    r = in_generic->id;
+    xfree(in_generic); in_generic = 0;
+    return r;
+  }
+  if (in_generic->id != ULS_XML_DATA) {
+    r = in_generic->id;
+    xfree(in_generic); in_generic = 0;
+    return -ULS_ERR_PROTOCOL;
+  }
+  if (in_size < sizeof(*in)) {
+    xfree(in_generic); in_generic = 0;
+    return -ULS_ERR_PROTOCOL;
+  }
+  in = (struct userlist_pk_xml_data*) in_generic; in_generic = 0;
+  if (in->info_len > MAX_XML_LEN) {
+    xfree(in); in = 0;
+    return -ULS_ERR_PROTOCOL;
+  }
+  if (in->info_len != strlen(in->data)) {
+    xfree(in); in = 0;
+    return -ULS_ERR_PROTOCOL;
+  }
+  *p_info = xstrdup(in->data);
+  xfree(in); in = 0;
+
+  return ULS_XML_DATA;
+}
+
diff -ruNp ejudge-2.3.18/userlist_clnt.h ejudge/userlist_clnt.h
--- ejudge-2.3.18/userlist_clnt.h	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/userlist_clnt.h	2011-07-11 09:02:04.293277996 +0400
@@ -1,10 +1,10 @@
 /* -*- c -*- */
-/* $Id: userlist_clnt.h 5948 2010-07-15 09:47:25Z cher $ */
+/* $Id: userlist_clnt.h 6316 2011-05-08 07:09:12Z cher $ */
 
 #ifndef __USERLIST_CLNT_H__
 #define __USERLIST_CLNT_H__
 
-/* Copyright (C) 2002-2010 Alexander Chernov <cher@ejudge.ru> */
+/* Copyright (C) 2002-2011 Alexander Chernov <cher@ejudge.ru> */
 
 /*
  * This program is free software; you can redistribute it and/or modify
@@ -395,4 +395,46 @@ userlist_clnt_get_xml_by_text(
         const unsigned char *request_text,
         unsigned char **reply_text);
 
+int
+userlist_clnt_list_users_2(
+        struct userlist_clnt *clnt,
+        int cmd,
+        int contest_id,
+        int group_id,
+        const unsigned char *filter,
+        int offset,
+        int count,
+        unsigned char **p_info);
+
+int
+userlist_clnt_get_count(
+        struct userlist_clnt *clnt,
+        int cmd,
+        int contest_id,
+        int group_id,
+        const unsigned char *filter,
+        long long *p_count);
+
+struct userlist_pk_create_user_2;
+int
+userlist_clnt_create_user_2(
+        struct userlist_clnt *clnt,
+        int cmd,
+        const struct userlist_pk_create_user_2 *params,
+        const unsigned char *login_str,
+        const unsigned char *email_str,
+        const unsigned char *reg_password_str,
+        const unsigned char *cnts_password_str,
+        const unsigned char *cnts_name_str,
+        int *p_user_id);
+int
+userlist_clnt_get_prev_user_id(
+        struct userlist_clnt *clnt,
+        int cmd,
+        int contest_id,
+        int group_id,
+        int user_id,
+        const unsigned char *filter,
+        int *p_user_id);
+
 #endif /* __USERLIST_CLNT_H__ */
diff -ruNp ejudge-2.3.18/userlist_proto.h ejudge/userlist_proto.h
--- ejudge-2.3.18/userlist_proto.h	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/userlist_proto.h	2011-07-11 09:02:04.176280147 +0400
@@ -1,10 +1,10 @@
 /* -*- c -*- */
-/* $Id: userlist_proto.h 5948 2010-07-15 09:47:25Z cher $ */
+/* $Id: userlist_proto.h 6377 2011-06-19 19:49:48Z cher $ */
 
 #ifndef __USERLIST_PROTO_H__
 #define __USERLIST_PROTO_H__
 
-/* Copyright (C) 2002-2010 Alexander Chernov <cher@ejudge.ru> */
+/* Copyright (C) 2002-2011 Alexander Chernov <cher@ejudge.ru> */
 
 /*
  * This program is free software; you can redistribute it and/or modify
@@ -108,6 +108,20 @@ enum
     ULS_CREATE_GROUP_MEMBER,
     ULS_DELETE_GROUP_MEMBER,
     ULS_GET_GROUPS,
+    ULS_LIST_ALL_USERS_2,
+    ULS_GET_USER_COUNT,
+    ULS_LIST_ALL_GROUPS_2,
+    ULS_GET_GROUP_COUNT,
+    ULS_PRIV_SET_REG_PASSWD_PLAIN,
+    ULS_PRIV_SET_REG_PASSWD_SHA1,
+    ULS_PRIV_SET_CNTS_PASSWD_PLAIN,
+    ULS_PRIV_SET_CNTS_PASSWD_SHA1,
+    ULS_CREATE_USER_2,
+    ULS_PREV_USER,
+    ULS_NEXT_USER,
+    ULS_LIST_ALL_USERS_3,
+    ULS_LIST_ALL_USERS_4,
+    ULS_GET_GROUP_INFO,
 
     ULS_LAST_CMD
   };
@@ -128,6 +142,7 @@ enum
     ULS_TEXT_DATA,
     ULS_NEW_PASSWORD,
     ULS_TEXT_DATA_FAILURE,
+    ULS_COUNT,
   };
 
 /* various error codes */
@@ -373,6 +388,54 @@ struct userlist_pk_dump_database
   int   html_flag;
 };
 
+struct userlist_pk_list_users_2
+{
+  short request_id;
+  int   contest_id;
+  int   group_id;
+  int   user_id;
+  int   filter_len;
+  int   offset;
+  int   count;
+  unsigned char data[1];
+};
+
+struct userlist_pk_create_user_2
+{
+  short request_id;
+  int login_len;
+  int email_len;
+  int send_email_flag;
+  int confirm_email_flag;
+  int random_password_flag;
+  int reg_password_len;
+  int use_sha1_flag;
+  int is_privileged_flag;
+  int is_invisible_flag;
+  int is_banned_flag;
+  int is_locked_flag;
+  int show_login_flag;
+  int show_email_flag;
+  int read_only_flag;
+  int never_clean_flag;
+  int simple_registration_flag;
+  int contest_id;
+  int cnts_status;
+  int cnts_is_invisible_flag;
+  int cnts_is_banned_flag;
+  int cnts_is_locked_flag;
+  int cnts_is_incomplete_flag;
+  int cnts_is_disqualified_flag;
+  int cnts_use_reg_passwd_flag;
+  int cnts_set_null_passwd_flag;
+  int cnts_random_password_flag;
+  int cnts_password_len;
+  int cnts_use_sha1_flag;
+  int cnts_name_len;
+  int group_id;
+  unsigned char data[5];
+};
+
 /* server->client replies */
 struct userlist_pk_login_ok
 {
@@ -445,4 +508,10 @@ struct userlist_pk_new_password
   char        data[3];
 };
 
+struct userlist_pk_count
+{
+  short reply_id;
+  long long count;
+};
+
 #endif /* __USERLIST_PROTO_H__ */
diff -ruNp ejudge-2.3.18/userlist-server.c ejudge/userlist-server.c
--- ejudge-2.3.18/userlist-server.c	2011-04-10 09:25:24.000000000 +0400
+++ ejudge/userlist-server.c	2011-07-11 09:02:04.325277406 +0400
@@ -1,5 +1,5 @@
 /* -*- mode: c -*- */
-/* $Id: userlist-server.c 6162 2011-03-27 07:07:27Z cher $ */
+/* $Id: userlist-server.c 6377 2011-06-19 19:49:48Z cher $ */
 
 /* Copyright (C) 2002-2011 Alexander Chernov <cher@ejudge.ru> */
 
@@ -42,6 +42,7 @@
 #include "csv.h"
 #include "sock_op.h"
 #include "compat.h"
+#include "bitset.h"
 
 #include "reuse_xalloc.h"
 #include "reuse_logger.h"
@@ -554,7 +555,7 @@ link_client_state(struct client_state *p
 #define default_sync() dflt_iface->sync(uldb_default->data)
 #define default_forced_sync() dflt_iface->forced_sync(uldb_default->data)
 #define default_get_login(a) dflt_iface->get_login(uldb_default->data, a)
-#define default_new_user(a,b,c,d) dflt_iface->new_user(uldb_default->data, a, b, c, d)
+#define default_new_user(a,b,c,d,e,f,g,h,i,j,k,l,m) dflt_iface->new_user(uldb_default->data, a, b, c, d, e, f, g, h, i, j, k, l, m)
 #define default_remove_user(a) dflt_iface->remove_user(uldb_default->data, a)
 #define default_get_cookie(a, b) dflt_iface->get_cookie(uldb_default->data, a, b)
 #define default_new_cookie(a, b, c, d, e, f, g, h, i, j, k, l) dflt_iface->new_cookie(uldb_default->data, a, b, c, d, e, f, g, h, i, j, k, l)
@@ -578,7 +579,7 @@ link_client_state(struct client_state *p
 #define default_check_user(a) dflt_iface->check_user(uldb_default->data, a)
 #define default_set_reg_passwd(a, b, c, d) dflt_iface->set_reg_passwd(uldb_default->data, a, b, c, d)
 #define default_set_team_passwd(a, b, c, d, e, f) dflt_iface->set_team_passwd(uldb_default->data, a, b, c, d, e, f)
-#define default_register_contest(a, b, c, d, e) dflt_iface->register_contest(uldb_default->data, a, b, c, d, e)
+#define default_register_contest(a, b, c, d, e, f) dflt_iface->register_contest(uldb_default->data, a, b, c, d, e, f)
 #define default_remove_member(a, b, c, d, e) dflt_iface->remove_member(uldb_default->data, a, b, c, d, e)
 #define default_is_read_only(a, b) dflt_iface->is_read_only(uldb_default->data, a, b)
 #define default_get_info_list_iterator(a, b) dflt_iface->get_info_list_iterator(uldb_default->data, a, b)
@@ -604,6 +605,10 @@ link_client_state(struct client_state *p
 #define default_get_contest_reg(a, b) dflt_iface->get_contest_reg(uldb_default->data, a, b)
 #define default_try_new_login(a, b, c, d, e) dflt_iface->try_new_login(uldb_default->data, a, b, c, d, e)
 #define default_set_simple_reg(a, b, c) dflt_iface->set_simple_reg(uldb_default->data, a, b, c)
+#define default_get_brief_list_iterator_2(a, b, c, d, e) dflt_iface->get_brief_list_iterator_2(uldb_default->data, a, b, c, d, e)
+#define default_get_user_count(a, b, c, d) dflt_iface->get_user_count(uldb_default->data, a, b, c, d)
+#define default_get_group_iterator_2(a, b, c) dflt_iface->get_group_iterator_2(uldb_default->data, a, b, c)
+#define default_get_group_count(a, b) dflt_iface->get_group_count(uldb_default->data, a, b)
 
 static void
 update_all_user_contests(int user_id)
@@ -1228,6 +1233,117 @@ check_pk_set_user_info(
 }
 
 static int
+check_pk_list_users_2(
+        struct client_state *p,
+        int pkt_len,
+        const struct userlist_pk_list_users_2 *data)
+{
+  int filter_len, exp_len;
+
+  if (pkt_len < sizeof(*data)) {
+    CONN_BAD("packet too small: %d instead of %d",
+             pkt_len, (int) sizeof(*data));
+    return -1;
+  }
+  if (pkt_len > (128*1024*1024)) {
+    CONN_BAD("packet too big: %d", pkt_len);
+    return -1;
+  }
+  if (data->filter_len < 0 || data->filter_len > (128*1024*1024)) {
+    CONN_BAD("filter_len is invalid: %d", data->filter_len);
+    return -1;
+  }
+  filter_len = strlen(data->data);
+  if (filter_len != data->filter_len) {
+    CONN_BAD("filter_len mismatch: %d instead of %d",
+             filter_len, data->filter_len);
+    return -1;
+  }
+  exp_len = sizeof(*data) + filter_len;
+  if (exp_len != pkt_len) {
+    CONN_BAD("pkt_len mismatch: %d instead of %d", pkt_len, exp_len);
+    return -1;
+  }
+
+  return 0;
+}
+
+static int
+check_pk_create_user_2(
+        struct client_state *p,
+        int pkt_len,
+        const struct userlist_pk_create_user_2 *data)
+{
+  if (pkt_len < sizeof(*data)) {
+    CONN_BAD("packet too small: %d instead of %d", pkt_len, (int) sizeof(*data));
+    return -1;
+  }
+  if (pkt_len > (128*1024*1024)) {
+    CONN_BAD("packet too big: %d", pkt_len);
+    return -1;
+  }
+
+  if (data->login_len < 0 || data->login_len > 65535) {
+    CONN_BAD("login_len is invalid: %d", data->login_len);
+    return -1;
+  }
+  if (data->email_len < 0 || data->email_len > 65535) {
+    CONN_BAD("email_len is invalid: %d", data->email_len);
+    return -1;
+  }
+  if (data->reg_password_len < 0 || data->reg_password_len > 65535) {
+    CONN_BAD("reg_password_len is invalid: %d", data->reg_password_len);
+    return -1;
+  }
+  if (data->cnts_password_len < 0 || data->cnts_password_len > 65535) {
+    CONN_BAD("cnts_password_len is invalid: %d", data->cnts_password_len);
+    return -1;
+  }
+  if (data->cnts_name_len < 0 || data->cnts_name_len > 65535) {
+    CONN_BAD("cnts_name_len is invalid: %d", data->cnts_name_len);
+    return -1;
+  }
+  int exp_len = sizeof(*data) + data->login_len + data->email_len + data->reg_password_len + data->cnts_password_len + data->cnts_name_len;
+  if (exp_len != pkt_len) {
+    CONN_BAD("pkt_len mismatch: %d instead of %d", pkt_len, exp_len);
+    return -1;
+  }
+
+  const unsigned char *str = data->data;
+  int len = strlen(str);
+  if (len != data->login_len) {
+    CONN_BAD("login_len mismatch: %d instead of %d", len, data->login_len);
+    return -1;
+  }
+  str += len + 1;
+  len = strlen(str);
+  if (len != data->email_len) {
+    CONN_BAD("email_len mismatch: %d instead of %d", len, data->email_len);
+    return -1;
+  }
+  str += len + 1;
+  len = strlen(str);
+  if (len != data->reg_password_len) {
+    CONN_BAD("reg_password_len mismatch: %d instead of %d", len, data->reg_password_len);
+    return -1;
+  }
+  str += len + 1;
+  len = strlen(str);
+  if (len != data->cnts_password_len) {
+    CONN_BAD("cnts_password_len mismatch: %d instead of %d", len, data->cnts_password_len);
+    return -1;
+  }
+  str += len + 1;
+  len = strlen(str);
+  if (len != data->cnts_name_len) {
+    CONN_BAD("cnts_name_len mismatch: %d instead of %d", len, data->cnts_name_len);
+    return -1;
+  }
+
+  return 0;
+}
+
+static int
 full_get_contest(
         struct client_state *p,
         const unsigned char *pfx,
@@ -1367,6 +1483,159 @@ get_email_sender(const struct contest_de
   return ppwd->pw_name;
 }
 
+static int
+send_registration_email(
+        const struct contest_desc *cnts,
+        const struct userlist_user *u,
+        int locale_id,
+        const unsigned char *self_url,
+        int confirm_action)
+{
+  struct sformat_extra_data sformat_data;
+  path_t email_template_path;
+  path_t email_template_locale_0_path;
+  char *email_template = 0;
+  size_t email_template_size = 0;
+
+  memset(&sformat_data, 0, sizeof(sformat_data));
+  sformat_data.locale_id = locale_id;
+  sformat_data.server_name = config->server_name;
+  sformat_data.server_name_en = config->server_name_en;
+
+  // load the registration letter template file
+  if (cnts && cnts->register_email_file) {
+    sformat_message(email_template_path, sizeof(email_template_path), 0,
+                    cnts->register_email_file, 0, 0, 0, 0, 0,
+                    u, cnts, &sformat_data);
+    if (generic_read_file(&email_template, 0, &email_template_size, 0, "", email_template_path, "") < 0) {
+      // the template file for the given locale_id does not exist, so try locale_id = 0
+      sformat_data.locale_id = 0;
+      sformat_message(email_template_locale_0_path, sizeof(email_template_locale_0_path), 0,
+                      cnts->register_email_file, 0, 0, 0, 0, 0,
+                      u, cnts, &sformat_data);
+      if (strcmp(email_template_path, email_template_locale_0_path) != 0) {
+        strcpy(email_template_path, email_template_locale_0_path);
+        if (generic_read_file(&email_template, 0, &email_template_size, 0, "", email_template_path, "") < 0) {
+          email_template = 0;
+          email_template_size = 0;
+        }
+      } else {
+        email_template = 0;
+        email_template_size = 0;
+      }
+    }
+  }
+  sformat_data.locale_id = locale_id;
+
+  // sanity checks
+  if (email_template_size > 1 * 1024 * 1024) {
+    xfree(email_template); email_template = 0;
+    email_template_size = 0;
+  }
+  if (email_template && strlen(email_template) != email_template_size) {
+    xfree(email_template); email_template = 0;
+    email_template_size = 0;
+  }
+  if (email_template && is_empty_string(email_template)) {
+    xfree(email_template); email_template = 0;
+    email_template_size = 0;
+  }
+
+  unsigned char contest_id_str[64] = { 0 };
+  if (cnts) {
+    snprintf(contest_id_str, sizeof(contest_id_str), "&contest_id=%d", cnts->id);
+  }
+  unsigned char locale_id_str[64] = { 0 };
+  if (locale_id > 0) {
+    snprintf(locale_id_str, sizeof(locale_id_str), "&locale_id=%d", locale_id);
+  }
+  const unsigned char *base_url = 0;
+  if (self_url && *self_url) {
+    base_url = self_url;
+  } else if (cnts && cnts->register_url) {
+    base_url = cnts->register_url;
+  } else if (config->register_url) {
+    base_url = config->register_url;
+  } else {
+    base_url = "http://localhost/cgi-bin/register";
+  }
+  if (confirm_action <= 0 && cnts && cnts->force_registration) confirm_action = 4;
+  if (confirm_action <= 0) confirm_action = 3;
+  unsigned char confirm_url_buf[1024];
+  snprintf(confirm_url_buf, sizeof(confirm_url_buf), "%s?action=%d&login=%s%s%s",
+           base_url, confirm_action, u->login, contest_id_str, locale_id_str);
+  sformat_data.url = confirm_url_buf;
+
+  unsigned char contest_main_url[1024] = { 0 };
+  if (cnts && cnts->main_url) {
+    snprintf(contest_main_url, sizeof(contest_main_url), " (%s)", cnts->main_url);
+  } else if (config->server_main_url) {
+    snprintf(contest_main_url, sizeof(contest_main_url), " (%s)", config->server_main_url);
+  }
+  sformat_data.str1 = contest_main_url;
+
+  l10n_setlocale(locale_id);
+  if (!email_template && cnts) {
+    email_template =
+      _("Hello,\n"
+        "\n"
+        "Somebody (probably you) have specified this e-mail address (%Ue)\n"
+        "when registering an account to participate in %LCn%V1.\n"
+        "\n"
+        "To confirm registration, you should enter the provided login\n"
+        "and password on the login page of the server at the\n"
+        "following url: %Vu.\n"
+        "\n"
+        "Note, that if you do not do this in 24 hours from the moment\n"
+        "of sending this letter, registration will be void.\n"
+        "\n"
+        "login:    %Ul\n"
+        "password: %Uz\n"
+          "\n"
+        "Regards,\n"
+        "The ejudge contest administration system (www.ejudge.ru)\n");
+    email_template = xstrdup(email_template);
+    email_template_size = strlen(email_template);
+  }
+  if (!email_template) {
+    email_template =
+      _("Hello,\n"
+        "\n"
+        "Somebody (probably you) have specified this e-mail address (%Ue)\n"
+        "when registering an account on the %LVn%V1.\n"
+        "\n"
+        "To confirm registration, you should enter the provided login\n"
+        "and password on the login page of the server at the\n"
+        "following url: %Vu.\n"
+        "\n"
+        "Note, that if you do not do this in 24 hours from the moment\n"
+        "of sending this letter, registration will be void.\n"
+        "\n"
+        "login:    %Ul\n"
+        "password: %Uz\n"
+        "\n"
+        "Regards,\n"
+        "The ejudge contest administration system (www.ejudge.ru)\n");
+    email_template = xstrdup(email_template);
+    email_template_size = strlen(email_template);
+  }
+
+  size_t email_text_size = email_template_size * 4;
+  if (email_text_size < 4096) email_text_size = 4096;
+  unsigned char *email_text = (unsigned char *) xcalloc(email_text_size, 1);
+  sformat_message(email_text, email_text_size, 0, email_template,
+                  0, 0, 0, 0, 0, u, cnts, &sformat_data);
+  unsigned char *email_subject = _("You have been registered");
+  l10n_setlocale(0);
+
+  const unsigned char *sender_address = get_email_sender(cnts);
+  int retval = send_email_message(u->email, sender_address, NULL, email_subject, email_text);
+  xfree(email_text); email_text = 0;
+  xfree(email_template); email_template = 0;
+
+  return retval;
+}
+
 static void
 cmd_register_new_2(struct client_state *p,
                    int pkt_len,
@@ -1486,7 +1755,8 @@ cmd_register_new_2(struct client_state *
 
   generate_random_password(8, passwd_buf);
   passwd_len = strlen(passwd_buf);
-  user_id = default_new_user(login, email, passwd_buf, 1);
+  user_id = default_new_user(login, email, USERLIST_PWD_PLAIN, passwd_buf,
+                             0, 0, 0, 0, 0, 0, 0, 0, 0);
 
   login_len = strlen(login);
   out_size = sizeof(*out) + login_len + passwd_len;
@@ -1762,7 +2032,7 @@ cmd_register_new(struct client_state *p,
   }
 
   generate_random_password(8, passwd_buf);
-  user_id = default_new_user(login, email, passwd_buf, 0);
+  user_id = default_new_user(login, email, USERLIST_PWD_PLAIN, passwd_buf, 0, 0, 0, 0, 0, 0, 0, 0, 0);
   if (default_get_user_info_1(user_id, &u) < 0 || !u) {
     send_reply(p, -ULS_ERR_DB_ERROR);
     err("%s -> database error", logbuf);
@@ -2362,7 +2632,11 @@ cmd_login(struct client_state *p,
     return;
   }
   rdtscll(tsc2);
-  tsc2 = (tsc2 - tsc1) * 1000000 / cpu_frequency;
+  if (cpu_frequency > 0) {
+    tsc2 = (tsc2 - tsc1) * 1000000 / cpu_frequency;
+  } else {
+    tsc2 = tsc2 - tsc1;
+  }
   if (ui) name = ui->name;
   if (!name || !*name) name = u->login;
   if (!name) name = "";
@@ -2477,7 +2751,11 @@ cmd_check_user(
     return;
   }
   rdtscll(tsc2);
-  tsc2 = (tsc2 - tsc1) * 1000000 / cpu_frequency;
+  if (cpu_frequency > 0) {
+    tsc2 = (tsc2 - tsc1) * 1000000 / cpu_frequency;
+  } else {
+    tsc2 = tsc2 - tsc1;
+  }
   if (ui) name = ui->name;
   if (!name || !*name) name = u->login;
   if (!name) name = "";
@@ -2619,7 +2897,11 @@ cmd_team_login(struct client_state *p, i
     return;
   }
   rdtscll(tsc2);
-  tsc2 = (tsc2 - tsc1) * 1000000 / cpu_frequency;
+  if (cpu_frequency > 0) {
+    tsc2 = (tsc2 - tsc1) * 1000000 / cpu_frequency;
+  } else {
+    tsc2 = tsc2 - tsc1;
+  }
   if (ui) name = ui->name;
   if (!name || !*name) name = u->login;
   if (!name) name = "";
@@ -2786,7 +3068,11 @@ cmd_team_check_user(struct client_state 
     return;
   }
   rdtscll(tsc2);
-  tsc2 = (tsc2 - tsc1) * 1000000 / cpu_frequency;
+  if (cpu_frequency > 0) {
+    tsc2 = (tsc2 - tsc1) * 1000000 / cpu_frequency;
+  } else {
+    tsc2 = tsc2 - tsc1;
+  }
   if (ui) name = ui->name;
   if (!name || !*name) name = u->login;
   if (!name) name = "";
@@ -2969,7 +3255,11 @@ cmd_priv_login(struct client_state *p, i
     return;
   }
   rdtscll(tsc2);
-  tsc2 = (tsc2 - tsc1) * 1000000 / cpu_frequency;
+  if (cpu_frequency > 0) {
+    tsc2 = (tsc2 - tsc1) * 1000000 / cpu_frequency;
+  } else {
+    tsc2 = tsc2 - tsc1;
+  }
 
   if (!u) {
     err("%s -> WRONG LOGIN", logbuf);
@@ -3187,7 +3477,11 @@ cmd_priv_check_user(struct client_state 
     return;
   }
   rdtscll(tsc2);
-  tsc2 = (tsc2 - tsc1) * 1000000 / cpu_frequency;
+  if (cpu_frequency > 0) {
+    tsc2 = (tsc2 - tsc1) * 1000000 / cpu_frequency;
+  } else {
+    tsc2 = tsc2 - tsc1;
+  }
 
   if (!u) {
     err("%s -> WRONG LOGIN", logbuf);
@@ -3295,7 +3589,11 @@ cmd_check_cookie(struct client_state *p,
     return;
   }
   rdtscll(tsc2);
-  tsc2 = (tsc2 - tsc1) * 1000000 / cpu_frequency;
+  if (cpu_frequency > 0) {
+    tsc2 = (tsc2 - tsc1) * 1000000 / cpu_frequency;
+  } else {
+    tsc2 = tsc2 - tsc1;
+  }
 
   if (data->contest_id < 0) data->contest_id = cookie->contest_id;
   orig_contest_id = data->contest_id;
@@ -3437,7 +3735,11 @@ cmd_team_check_cookie(
     return;
   }
   rdtscll(tsc2);
-  tsc2 = (tsc2 - tsc1) * 1000000 / cpu_frequency;
+  if (cpu_frequency > 0) {
+    tsc2 = (tsc2 - tsc1) * 1000000 / cpu_frequency;
+  } else {
+    tsc2 = tsc2 - tsc1;
+  }
 
   if (data->contest_id < 0) data->contest_id = cookie->contest_id;
   orig_contest_id = data->contest_id;
@@ -3600,7 +3902,11 @@ cmd_priv_check_cookie(struct client_stat
     return;
   }
   rdtscll(tsc2);
-  tsc2 = (tsc2 - tsc1) * 1000000 / cpu_frequency;
+  if (cpu_frequency > 0) {
+    tsc2 = (tsc2 - tsc1) * 1000000 / cpu_frequency;
+  } else {
+    tsc2 = tsc2 - tsc1;
+  }
 
   if (data->contest_id < 0) data->contest_id = cookie->contest_id;
   orig_contest_id = data->contest_id;
@@ -3784,7 +4090,11 @@ cmd_priv_cookie_login(struct client_stat
     return;
   }
   rdtscll(tsc2);
-  tsc2 = (tsc2 - tsc1) * 1000000 / cpu_frequency;
+  if (cpu_frequency > 0) {
+    tsc2 = (tsc2 - tsc1) * 1000000 / cpu_frequency;
+  } else {
+    tsc2 = tsc2 - tsc1;
+  }
 
   if (default_get_user_info_3(cookie->user_id,data->contest_id,&u,&ui,&c) < 0
       || !u) {
@@ -4623,7 +4933,7 @@ cmd_register_contest(
 
   if (c->autoregister) status = USERLIST_REG_OK;
   errcode = default_register_contest(data->user_id, data->contest_id,
-                                     status, cur_time, &r);
+                                     status, 0, cur_time, &r);
   if (errcode < 0) {
     err("%s -> registration failed", logbuf);
     send_reply(p, -ULS_ERR_UNSPECIFIED_ERROR);
@@ -4697,7 +5007,7 @@ cmd_register_contest_2(
 
   if (c->autoregister) status = USERLIST_REG_OK;
   errcode = default_register_contest(data->user_id, data->contest_id,
-                                     status, cur_time, &r);
+                                     status, 0, cur_time, &r);
   if (errcode < 0) {
     err("%s -> registration failed", logbuf);
     send_reply(p, -ULS_ERR_UNSPECIFIED_ERROR);
@@ -4710,9 +5020,7 @@ cmd_register_contest_2(
 
   default_check_user_reg_data(data->user_id, data->contest_id);
   r = default_get_contest_reg(data->user_id, data->contest_id);
-  if (r && r->status == USERLIST_REG_OK) {
-    update_userlist_table(data->contest_id);
-  }
+  update_userlist_table(data->contest_id);
   info("%s -> OK", logbuf);
   send_reply(p, ULS_OK);
   return;
@@ -4748,7 +5056,7 @@ cmd_priv_register_contest(
   if (is_cnts_capable(p, c, bit, logbuf) < 0) return;
 
   if (c->autoregister) status = USERLIST_REG_OK;
-  status = default_register_contest(data->user_id, data->contest_id, status,
+  status = default_register_contest(data->user_id, data->contest_id, status, 0,
                                     cur_time, &r);
   if (status < 0) {
     err("%s -> registration failed", logbuf);
@@ -4760,7 +5068,7 @@ cmd_priv_register_contest(
     return;
   }
 
-  if (r->status == USERLIST_REG_OK) {
+  if (r && r->status == USERLIST_REG_OK) {
     update_userlist_table(data->contest_id);
   }
   info("%s -> OK", logbuf);
@@ -4816,6 +5124,7 @@ cmd_delete_member(struct client_state *p
     return send_reply(p, -ULS_ERR_UNSPECIFIED_ERROR);
   }
 
+  update_userlist_table(data->contest_id);
   if (cloned_flag) reply_code = ULS_CLONED;
   info("%s -> OK", logbuf);
   send_reply(p, reply_code);
@@ -6312,7 +6621,7 @@ cmd_edit_registration(struct client_stat
     send_reply(p, -ULS_ERR_PROTOCOL);
     return;
   }
-  if (data->flags_cmd < 0 || data->flags_cmd > 3) {
+  if (data->flags_cmd < 0 || data->flags_cmd > 4) {
     err("%s -> invalid flags command", logbuf);
     send_reply(p, -ULS_ERR_PROTOCOL);
     return;
@@ -6788,7 +7097,7 @@ cmd_create_user(
     login_ptr = buf;
   }
 
-  if ((user_id = default_new_user(login_ptr, "N/A", NULL, 0)) <= 0) {
+  if ((user_id = default_new_user(login_ptr, "N/A", USERLIST_PWD_PLAIN, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0)) <= 0) {
     err("%s -> cannot create user", logbuf);
     send_reply(p, -ULS_ERR_NO_PERMS);
     return;
@@ -7378,7 +7687,11 @@ cmd_get_cookie(struct client_state *p,
   if (default_get_cookie(data->cookie, &cookie) < 0 || !cookie)
     FAIL(ULS_ERR_NO_COOKIE, "no such cookie");
   rdtscll(tsc2);
-  tsc2 = (tsc2 - tsc1) * 1000000 / cpu_frequency;
+  if (cpu_frequency > 0) {
+    tsc2 = (tsc2 - tsc1) * 1000000 / cpu_frequency;
+  } else {
+    tsc2 = tsc2 - tsc1;
+  }
 
   new_contest_id = cookie->contest_id;
   if (cookie->contest_id > 0) {
@@ -7700,6 +8013,120 @@ cmd_priv_set_passwd(
   info("%s -> OK, %d", logbuf, cloned_flag);
 }
 
+static void
+cmd_priv_set_passwd_2(
+        struct client_state *p,
+        int pkt_len,
+        struct userlist_pk_set_password *data)
+{
+  const unsigned char *old_pwd, *new_pwd;
+  unsigned char logbuf[1024];
+  struct passwd_internal newint;
+  const struct userlist_user *u = 0;
+  const struct contest_desc *cnts = 0;
+  const struct userlist_user_info *ui = 0;
+  const struct userlist_contest *c = 0;
+  int reply_code = ULS_OK, cloned_flag = 0, contest_id = 0;
+
+  old_pwd = data->data;
+  new_pwd = old_pwd + data->old_len + 1;
+
+  if (!data->user_id) data->user_id = p->user_id;
+  snprintf(logbuf, sizeof(logbuf), "PRIV_SET_PASSWD_2: %d, %d, %d",
+           data->request_id, data->user_id, data->contest_id);
+
+  if (is_admin(p, logbuf) < 0) return;
+
+  if (data->new_len <= 0) {
+    err("%s -> new password is empty", logbuf);
+    send_reply(p, -ULS_ERR_INVALID_PASSWORD);
+    return;
+  }
+  if (passwd_convert_to_internal(new_pwd, &newint) < 0) {
+    err("%s -> new password is invalid", logbuf);
+    send_reply(p, -ULS_ERR_INVALID_PASSWORD);
+    return;
+  }
+
+  switch (data->request_id) {
+  case ULS_PRIV_SET_REG_PASSWD_PLAIN:
+  case ULS_PRIV_SET_REG_PASSWD_SHA1:
+    if (default_get_user_info_1(data->user_id, &u) < 0 || !u) {
+      err("%s -> invalid user", logbuf);
+      send_reply(p, -ULS_ERR_BAD_UID);
+      return;
+    }
+    if (data->user_id != p->user_id) {
+      if (is_privileged_user(u) >= 0) {
+        if (is_db_capable(p, OPCAP_PRIV_EDIT_PASSWD, logbuf) < 0) return;
+      } else {
+        if (is_db_capable(p, OPCAP_EDIT_PASSWD, logbuf) < 0) return;
+      }
+    }
+
+    if (data->request_id == ULS_PRIV_SET_REG_PASSWD_PLAIN) {
+      default_set_reg_passwd(u->id, USERLIST_PWD_PLAIN,
+                             newint.pwds[USERLIST_PWD_PLAIN], cur_time);
+    } else if (data->request_id == ULS_PRIV_SET_REG_PASSWD_SHA1) {
+      default_set_reg_passwd(u->id, USERLIST_PWD_SHA1,
+                             newint.pwds[USERLIST_PWD_SHA1], cur_time);
+    } else {
+      abort();
+    }
+    break;
+
+  case ULS_PRIV_SET_CNTS_PASSWD_PLAIN:
+  case ULS_PRIV_SET_CNTS_PASSWD_SHA1:
+    contest_id = data->contest_id;
+    if (contest_id > 0) {
+      if (full_get_contest(p, logbuf, &contest_id, &cnts) < 0) return;
+      if (cnts->disable_team_password) {
+        err("%s -> team password is disabled", logbuf);
+        send_reply(p, -ULS_ERR_NO_PERMS);
+        return;
+      }
+    }
+
+    if (default_get_user_info_3(data->user_id, contest_id, &u, &ui, &c) < 0
+        || !u) {
+      err("%s -> invalid user", logbuf);
+      send_reply(p, -ULS_ERR_BAD_UID);
+      return;
+    }
+    if (data->user_id != p->user_id) {
+      if (is_privileged_cnts_user(u, cnts) >= 0) {
+        if (is_dbcnts_capable(p, cnts, OPCAP_PRIV_EDIT_PASSWD, logbuf) < 0)
+          return;
+      } else {
+        if (is_dbcnts_capable(p, cnts, OPCAP_EDIT_PASSWD, logbuf) < 0) return;
+      }
+    }
+    if (contest_id > 0 && (!c || c->status != USERLIST_REG_OK)) {
+      err("%s -> not registered", logbuf);
+      send_reply(p, -ULS_ERR_NOT_REGISTERED);
+      return;
+    }
+
+    if (data->request_id == ULS_PRIV_SET_CNTS_PASSWD_PLAIN) {
+      default_set_team_passwd(data->user_id, contest_id, USERLIST_PWD_PLAIN,
+                              newint.pwds[USERLIST_PWD_PLAIN], cur_time,
+                              &cloned_flag);
+    } else if (data->request_id == ULS_PRIV_SET_CNTS_PASSWD_SHA1) {
+      default_set_team_passwd(data->user_id, contest_id, USERLIST_PWD_SHA1,
+                              newint.pwds[USERLIST_PWD_SHA1], cur_time,
+                              &cloned_flag);
+    }
+    if (cloned_flag) reply_code = ULS_CLONED;
+    break;
+
+  default:
+    abort();
+  }
+
+  default_remove_user_cookies(data->user_id);
+  send_reply(p, reply_code);
+  info("%s -> OK, %d", logbuf, cloned_flag);
+}
 
 static void
 do_get_database(FILE *f, int contest_id, const struct contest_desc *cnts)
@@ -8958,6 +9385,618 @@ cleanup:
   xfree(xml_text);
 }
 
+static void
+cmd_list_all_users_2(
+        struct client_state *p,
+        int pkt_len,
+        struct userlist_pk_list_users_2 *data)
+{
+  FILE *f = 0;
+  char *xml_ptr = 0;
+  size_t xml_size = 0;
+  struct userlist_pk_xml_data *out = 0;
+  size_t out_size = 0;
+  const struct contest_desc *cnts = 0;
+  unsigned char logbuf[1024];
+  ptr_iterator_t iter;
+  const struct userlist_user *u;
+
+  snprintf(logbuf, sizeof(logbuf), "LIST_ALL_USERS_2: %d, %d, %d, %d, %d",
+           p->user_id, data->contest_id, data->group_id, data->offset, data->count);
+
+  if (is_judge(p, logbuf) < 0) return;
+  if (data->contest_id > 0) {
+    if (full_get_contest(p, logbuf, &data->contest_id, &cnts) < 0) return;
+  }
+  if (is_dbcnts_capable(p, cnts, OPCAP_LIST_USERS, logbuf) < 0) return;
+
+  f = open_memstream(&xml_ptr, &xml_size);
+  userlist_write_xml_header(f);
+  iter = default_get_brief_list_iterator_2(data->contest_id, data->group_id, data->data, data->offset, data->count);
+  if (iter) {
+    for (; iter->has_next(iter); iter->next(iter)) {
+      if (!(u = (const struct userlist_user*) iter->get(iter))) continue;
+      userlist_unparse_user_short(u, f, data->contest_id);
+      default_unlock_user(u);
+    }
+  }
+  userlist_write_xml_footer(f);
+  if (iter) iter->destroy(iter);
+  close_memstream(f); f = 0;
+  ASSERT(xml_size == strlen(xml_ptr));
+  out_size = sizeof(*out) + xml_size;
+  out = (struct userlist_pk_xml_data*) xmalloc(out_size);
+  memset(out, 0, out_size);
+  out->reply_id = ULS_XML_DATA;
+  out->info_len = xml_size;
+  memcpy(out->data, xml_ptr, xml_size + 1);
+  xfree(xml_ptr); xml_ptr = 0;
+  enqueue_reply_to_client(p, out_size, out);
+  info("%s -> OK, size = %zu", logbuf, xml_size); 
+  xfree(out); out = 0;
+}
+
+static void
+cmd_get_user_count(
+        struct client_state *p,
+        int pkt_len,
+        struct userlist_pk_list_users_2 *data)
+{
+  struct userlist_pk_count out;
+  const struct contest_desc *cnts = 0;
+  unsigned char logbuf[1024];
+  long long count = -1;
+  int r;
+
+  snprintf(logbuf, sizeof(logbuf), "GET_USER_COUNT: %d, %d",
+           p->user_id, data->contest_id);
+
+  if (is_judge(p, logbuf) < 0) return;
+  if (data->contest_id) {
+    if (full_get_contest(p, logbuf, &data->contest_id, &cnts) < 0) return;
+  }
+  if (is_dbcnts_capable(p, cnts, OPCAP_LIST_USERS, logbuf) < 0) return;
+
+  r = default_get_user_count(data->contest_id, data->group_id, data->data, &count);
+  if (r < 0) {
+    err("%s -> database error %d", logbuf, -r);
+    send_reply(p, -ULS_ERR_DB_ERROR);
+    return;
+  }
+  if (count < 0) {
+    err("%s -> invalid value of count %lld", logbuf, count);
+    send_reply(p, -ULS_ERR_DB_ERROR);
+    return;    
+  }
+
+  memset(&out, 0, sizeof(out));
+  out.reply_id = ULS_COUNT;
+  out.count = count;
+  enqueue_reply_to_client(p, sizeof(out), &out);
+  info("%s -> OK, %lld", logbuf, out.count); 
+}
+
+static void
+cmd_list_all_groups_2(
+        struct client_state *p,
+        int pkt_len,
+        struct userlist_pk_list_users_2 *data)
+{
+  FILE *fout = 0;
+  char *xml_ptr = 0;
+  size_t xml_size = 0;
+  struct userlist_pk_xml_data *out = 0;
+  size_t out_size = 0;
+  unsigned char logbuf[1024];
+  ptr_iterator_t iter;
+  const struct userlist_group *grp;
+
+  snprintf(logbuf, sizeof(logbuf), "LIST_ALL_GROUPS_2: %d, %d, %d",
+           p->user_id, data->offset, data->count);
+
+  if (is_judge(p, logbuf) < 0) return;
+  if (is_dbcnts_capable(p, NULL, OPCAP_LIST_USERS, logbuf) < 0) return;
+
+  fout = open_memstream(&xml_ptr, &xml_size);
+  userlist_write_xml_header(fout);
+  userlist_write_groups_header(fout);
+  iter = default_get_group_iterator_2(data->data, data->offset, data->count);
+  if (iter) {
+    for (; iter->has_next(iter); iter->next(iter)) {
+      grp = (const struct userlist_group*) iter->get(iter);
+      if (grp) {
+        userlist_unparse_usergroup(fout, grp, "      ", "\n");
+        // plugin_call1(unlock_group, grp);
+      }
+    }
+    iter->destroy(iter); iter = 0;
+  }
+  userlist_write_groups_footer(fout);
+  userlist_write_xml_footer(fout);
+  fclose(fout); fout = 0;
+
+  ASSERT(xml_size == strlen(xml_ptr));
+  out_size = sizeof(*out) + xml_size;
+  out = (struct userlist_pk_xml_data*) xmalloc(out_size);
+  memset(out, 0, out_size);
+  out->reply_id = ULS_XML_DATA;
+  out->info_len = xml_size;
+  memcpy(out->data, xml_ptr, xml_size + 1);
+  xfree(xml_ptr); xml_ptr = 0;
+  enqueue_reply_to_client(p, out_size, out);
+  info("%s -> OK, size = %zu", logbuf, xml_size); 
+  xfree(out); out = 0;
+}
+
+static void
+cmd_get_group_count(
+        struct client_state *p,
+        int pkt_len,
+        struct userlist_pk_list_users_2 *data)
+{
+  struct userlist_pk_count out;
+  unsigned char logbuf[1024];
+  long long count = -1;
+  int r;
+
+  snprintf(logbuf, sizeof(logbuf), "GET_GROUP_COUNT: %d", p->user_id);
+
+  if (is_judge(p, logbuf) < 0) return;
+  if (is_dbcnts_capable(p, NULL, OPCAP_LIST_USERS, logbuf) < 0) return;
+
+  r = default_get_group_count(data->data, &count);
+  if (r < 0) {
+    err("%s -> database error %d", logbuf, -r);
+    send_reply(p, -ULS_ERR_DB_ERROR);
+    return;
+  }
+  if (count < 0) {
+    err("%s -> invalid value of count %lld", logbuf, count);
+    send_reply(p, -ULS_ERR_DB_ERROR);
+    return;    
+  }
+
+  memset(&out, 0, sizeof(out));
+  out.reply_id = ULS_COUNT;
+  out.count = count;
+  enqueue_reply_to_client(p, sizeof(out), &out);
+  info("%s -> OK, %lld", logbuf, out.count); 
+}
+
+static void
+cmd_create_user_2(
+        struct client_state *p,
+        int pkt_len,
+        struct userlist_pk_create_user_2 *data)
+{
+  unsigned char logbuf[1024];
+  const unsigned char *login_str = data->data;
+  const unsigned char *email_str = login_str + data->login_len + 1;
+  const unsigned char *reg_password_str = email_str + data->email_len + 1;
+  const unsigned char *cnts_password_str = reg_password_str + data->reg_password_len + 1;
+  const unsigned char *cnts_name_str = cnts_password_str + data->cnts_password_len + 1;
+  int user_id = 0;
+  unsigned char random_reg_password_buf[64];
+  int reg_password_len = data->reg_password_len;
+  unsigned char sha1_reg_password_buf[128];
+  int reg_password_method = USERLIST_PWD_PLAIN;
+  const struct contest_desc *cnts = 0;
+  int login_len = data->login_len;
+  unsigned char auto_login_buf[64];
+  const struct userlist_contest *cnts_reg = 0;
+  const struct userlist_group *ul_group = 0;
+  int cnts_password_len = data->cnts_password_len;
+  int cnts_password_method = USERLIST_PWD_PLAIN;
+  unsigned char random_cnts_password_buf[64];
+  unsigned char sha1_cnts_password_buf[64];
+  int cloned_flag = 0;
+  int send_email_flag = data->send_email_flag;
+
+  snprintf(logbuf, sizeof(logbuf), "CREATE_USER_2: %d", p->user_id);
+
+  if (p->user_id < 0) {
+    err("%s -> not authentificated", logbuf);
+    send_reply(p, -ULS_ERR_NO_PERMS);
+    return;
+  }
+  ASSERT(p->user_id > 0);
+  if (is_db_capable(p, OPCAP_CREATE_USER, logbuf) < 0) return;
+
+  if (data->contest_id != 0) {
+    if (contests_get(data->contest_id, &cnts) < 0 || !cnts) {
+      err("%s -> invalid contest %d", logbuf, data->contest_id);
+      send_reply(p, -ULS_ERR_BAD_CONTEST_ID);
+      return;
+    }
+  }
+  if (data->group_id != 0) {
+    ul_group = plugin_call(get_group, data->contest_id);
+    if (!ul_group) {
+      err("%s -> invalid group %d", logbuf, data->group_id);
+      send_reply(p, -ULS_ERR_BAD_GROUP_ID);
+      return;
+    }
+  }
+
+  if (!login_len && cnts && cnts->assign_logins && cnts->login_template) {
+    int serial = 0;
+    int serial_step = 1;
+    int n = 0;
+    if (cnts->login_template_options
+        && sscanf(cnts->login_template_options, "%d%d%n",
+                  &serial, &serial_step, &n) == 2
+        && !cnts->login_template_options[n] && serial_step != 0) {
+      serial -= serial_step;
+    } else {
+      serial = 0;
+      serial_step = 1;
+    }
+    if (dflt_iface->try_new_login) {
+      serial += serial_step;
+      if (default_try_new_login(auto_login_buf, sizeof(auto_login_buf), cnts->login_template, serial, serial_step) < 0) {
+        send_reply(p, -ULS_ERR_DB_ERROR);
+        err("%s -> database error", logbuf);
+        return;
+      }
+    } else {
+      while (1) {
+        serial += serial_step;
+        snprintf(auto_login_buf, sizeof(auto_login_buf), cnts->login_template, serial);
+        if ((user_id = default_get_user_by_login(auto_login_buf)) < 0) break;
+      }
+    }
+    login_str = auto_login_buf;
+    login_len = strlen(login_str);
+  }
+
+  if (!login_len) {
+    err("%s -> empty login", logbuf);
+    send_reply(p, -ULS_ERR_INVALID_LOGIN);
+    return;
+  }
+  if (default_get_user_by_login(login_str) >= 0) {
+    err("%s -> login already exists", logbuf);
+    send_reply(p, -ULS_ERR_LOGIN_USED);
+    return;
+  }
+
+  if (data->random_password_flag) {
+    generate_random_password(16, random_reg_password_buf);
+    reg_password_str = random_reg_password_buf;
+    reg_password_len = strlen(reg_password_str);
+  }
+  if (!reg_password_len) {
+    err("%s -> empty password", logbuf);
+    send_reply(p, -ULS_ERR_INVALID_PASSWORD);
+    return;
+  }
+  if (data->use_sha1_flag) {
+    make_sha1_ascii(reg_password_str, reg_password_len, sha1_reg_password_buf);
+    reg_password_method = USERLIST_PWD_SHA1;
+    reg_password_str = sha1_reg_password_buf;
+    reg_password_len = strlen(reg_password_str);
+  }
+
+  user_id = default_new_user(login_str,
+                             email_str,
+                             reg_password_method,
+                             reg_password_str,
+                             data->is_privileged_flag,
+                             data->is_invisible_flag,
+                             data->is_banned_flag,
+                             data->is_locked_flag,
+                             data->show_login_flag,
+                             data->show_email_flag,
+                             data->read_only_flag,
+                             data->never_clean_flag,
+                             data->simple_registration_flag);
+  if (user_id <= 0) {
+    err("%s -> cannot create user", logbuf);
+    send_reply(p, -ULS_ERR_DB_ERROR);
+    return;
+  }
+
+  if (data->contest_id) {
+    int cnts_flags = 0;
+    if (data->cnts_is_invisible_flag) cnts_flags |= USERLIST_UC_INVISIBLE;
+    if (data->cnts_is_banned_flag) cnts_flags |= USERLIST_UC_BANNED;
+    if (data->cnts_is_locked_flag) cnts_flags |= USERLIST_UC_LOCKED;
+    if (data->cnts_is_incomplete_flag) cnts_flags |= USERLIST_UC_INCOMPLETE;
+    if (data->cnts_is_disqualified_flag) cnts_flags |= USERLIST_UC_DISQUALIFIED;
+    if (default_register_contest(user_id, data->contest_id, data->cnts_status, cnts_flags,
+                                 cur_time, &cnts_reg) < 0) {
+      err("%s -> cannot register user", logbuf);
+      send_reply(p, -ULS_ERR_DB_ERROR);
+      return;
+    }
+  }
+
+  if (cnts && !cnts->disable_team_password) {
+    if (data->cnts_use_reg_passwd_flag) {
+      default_set_team_passwd(user_id, data->contest_id, reg_password_method, reg_password_str,
+                              cur_time, &cloned_flag);
+    } else if (data->cnts_set_null_passwd_flag) {
+      // do nothing...
+    } else {
+      if (data->cnts_random_password_flag) {
+        generate_random_password(16, random_cnts_password_buf);
+        cnts_password_str = random_cnts_password_buf;
+        cnts_password_len = strlen(cnts_password_str);
+      }
+      if (data->cnts_use_sha1_flag) {
+        make_sha1_ascii(cnts_password_str, cnts_password_len, sha1_cnts_password_buf);
+        cnts_password_method = USERLIST_PWD_SHA1;
+        cnts_password_str = sha1_cnts_password_buf;
+        cnts_password_len = strlen(cnts_password_str);
+      }
+      default_set_team_passwd(user_id, data->contest_id, cnts_password_method, cnts_password_str,
+                              cur_time, &cloned_flag);
+    }
+  }
+
+  if (cnts && cnts_name_str && *cnts_name_str) {
+    if (default_set_user_info_field(user_id, data->contest_id,
+                                    USERLIST_NC_NAME, cnts_name_str,
+                                    cur_time, &cloned_flag) < 0) {
+      err("%s -> cannot set user name", logbuf);
+      send_reply(p, -ULS_ERR_DB_ERROR);
+      return;
+    }
+    if (default_set_user_info_field(user_id, 0,
+                                    USERLIST_NC_NAME, cnts_name_str,
+                                    cur_time, &cloned_flag) < 0) {
+      err("%s -> cannot set user name", logbuf);
+      send_reply(p, -ULS_ERR_DB_ERROR);
+      return;
+    }
+  }
+
+  if (ul_group) {
+    if (plugin_call(create_group_member, data->group_id, user_id) < 0) {
+      err("%s -> cannot add user to a group", logbuf);
+      send_reply(p, -ULS_ERR_DB_ERROR);
+      return;
+    }
+  }
+
+  const struct userlist_user *u = 0;
+  if (default_get_user_info_1(user_id, &u) < 0 || !u) {
+    send_reply(p, -ULS_ERR_DB_ERROR);
+    err("%s -> database error", logbuf);
+    return;
+  }
+
+  if (!email_str || !*email_str) send_email_flag = 0;
+  /* FIXME: check other conditions when email is not send */
+
+  if (send_email_flag) {
+    send_registration_email(cnts, u, 0, NULL, 0);
+  }
+
+  if (!send_email_flag || !data->confirm_email_flag) {
+    default_touch_login_time(user_id, 0, cur_time);
+  }
+
+  struct userlist_pk_login_ok out;
+  memset(&out, 0, sizeof(out));
+  out.reply_id = ULS_LOGIN_OK;
+  out.user_id = user_id;
+  enqueue_reply_to_client(p, sizeof(out), &out);
+  info("%s -> OK, %d", logbuf, user_id); 
+}
+
+static void
+cmd_next_user(
+        struct client_state *p,
+        int pkt_len,
+        struct userlist_pk_list_users_2 *data)
+{
+  unsigned char logbuf[1024];
+  const struct contest_desc *cnts = 0;
+  int user_id = 0;
+
+  snprintf(logbuf, sizeof(logbuf), "NEXT_USER: %d, %d, %d, %d", p->user_id,
+           data->user_id, data->contest_id, data->group_id);
+
+  if (is_judge(p, logbuf) < 0) return;
+  if (data->contest_id) {
+    if (full_get_contest(p, logbuf, &data->contest_id, &cnts) < 0) return;
+  }
+  if (is_dbcnts_capable(p, cnts, OPCAP_LIST_USERS, logbuf) < 0) return;
+
+  int (*func)(void *, int contest_id, int group_id, int user_id, const unsigned char *filter, int *p_user_id);
+  switch (data->request_id) {
+  case ULS_PREV_USER:
+    func = plugin_func(get_prev_user_id);
+    break;
+  case ULS_NEXT_USER:
+    func = plugin_func(get_next_user_id);
+    break;
+  default:
+    send_reply(p, -ULS_ERR_PROTOCOL);
+    err("%s -> invalid request", logbuf);
+    return;
+  }
+
+  struct userlist_pk_login_ok out;
+  memset(&out, 0, sizeof(out));
+
+  if (!func) {
+    out.reply_id = ULS_LOGIN_OK;
+    out.user_id = 0;
+    enqueue_reply_to_client(p, sizeof(out), &out);
+    info("%s -> not implemented, %d", logbuf, 0); 
+    return;
+  }
+
+  if (func(uldb_default->data, data->contest_id, data->group_id, data->user_id, data->data, &user_id) < 0) {
+    err("%s -> database error", logbuf);
+    send_reply(p, -ULS_ERR_DB_ERROR);
+    return;
+  }
+
+  out.reply_id = ULS_LOGIN_OK;
+  out.user_id = user_id;
+  enqueue_reply_to_client(p, sizeof(out), &out);
+  info("%s -> OK, %d", logbuf, user_id); 
+}
+
+static void
+cmd_list_all_users_3(
+        struct client_state *p,
+        int pkt_len,
+        struct userlist_pk_list_users_2 *data)
+{
+  FILE *f = 0;
+  char *xml_ptr = 0;
+  size_t xml_size = 0;
+  struct userlist_pk_xml_data *out = 0;
+  size_t out_size = 0;
+  const struct contest_desc *cnts = 0;
+  unsigned char logbuf[1024];
+  const struct userlist_user *u = 0;
+  bitset_t marked = BITSET_INITIALIZER;
+  int user_id;
+
+  snprintf(logbuf, sizeof(logbuf), "LIST_ALL_USERS_3: %d, %d, %d, %d, %d",
+           p->user_id, data->contest_id, data->group_id, data->offset, data->count);
+
+  if (is_judge(p, logbuf) < 0) return;
+  if (data->contest_id > 0) {
+    if (full_get_contest(p, logbuf, &data->contest_id, &cnts) < 0) return;
+  }
+  if (is_dbcnts_capable(p, cnts, OPCAP_LIST_USERS, logbuf) < 0) return;
+
+  bitset_url_decode(data->data, &marked);
+  f = open_memstream(&xml_ptr, &xml_size);
+  userlist_write_xml_header(f);
+  if (marked.size > 0) {
+    for (user_id = 1; user_id < marked.size; ++user_id) {
+      if (bitset_get(&marked, user_id)) {
+        if (default_get_user_info_4(user_id, data->contest_id, &u) >= 0 && u) {
+          userlist_unparse_user_short(u, f, data->contest_id);
+          default_unlock_user(u);
+        }
+      }
+    }
+  }
+  userlist_write_xml_footer(f);
+  close_memstream(f); f = 0;
+  ASSERT(xml_size == strlen(xml_ptr));
+  out_size = sizeof(*out) + xml_size;
+  out = (struct userlist_pk_xml_data*) xmalloc(out_size);
+  memset(out, 0, out_size);
+  out->reply_id = ULS_XML_DATA;
+  out->info_len = xml_size;
+  memcpy(out->data, xml_ptr, xml_size + 1);
+  xfree(xml_ptr); xml_ptr = 0;
+  enqueue_reply_to_client(p, out_size, out);
+  info("%s -> OK, size = %zu", logbuf, xml_size); 
+  xfree(out); out = 0;
+  bitset_free(&marked);
+}
+
+static void
+cmd_list_all_users_4(
+        struct client_state *p,
+        int pkt_len,
+        struct userlist_pk_list_users_2 *data)
+{
+  FILE *f = 0;
+  char *xml_ptr = 0;
+  size_t xml_size = 0;
+  struct userlist_pk_xml_data *out = 0;
+  size_t out_size = 0;
+  const struct contest_desc *cnts = 0;
+  unsigned char logbuf[1024];
+  const struct userlist_user *u = 0;
+  bitset_t marked = BITSET_INITIALIZER;
+  int user_id;
+
+  snprintf(logbuf, sizeof(logbuf), "LIST_ALL_USERS_4: %d, %d, %d, %d, %d",
+           p->user_id, data->contest_id, data->group_id, data->offset, data->count);
+
+  if (is_judge(p, logbuf) < 0) return;
+  if (data->contest_id > 0) {
+    if (full_get_contest(p, logbuf, &data->contest_id, &cnts) < 0) return;
+  }
+  if (is_dbcnts_capable(p, cnts, OPCAP_LIST_USERS, logbuf) < 0) return;
+
+  bitset_url_decode(data->data, &marked);
+  f = open_memstream(&xml_ptr, &xml_size);
+  userlist_write_xml_header(f);
+  if (marked.size > 0) {
+    for (user_id = 1; user_id < marked.size; ++user_id) {
+      if (bitset_get(&marked, user_id)) {
+        if (default_get_user_info_5(user_id, data->contest_id, &u) >= 0 && u) {
+          userlist_real_unparse_user(u, f, USERLIST_MODE_ALL, data->contest_id,
+                                     USERLIST_SHOW_REG_PASSWD | USERLIST_SHOW_CNTS_PASSWD);
+          default_unlock_user(u);
+        }
+      }
+    }
+  }
+  userlist_write_xml_footer(f);
+  close_memstream(f); f = 0;
+  ASSERT(xml_size == strlen(xml_ptr));
+  out_size = sizeof(*out) + xml_size;
+  out = (struct userlist_pk_xml_data*) xmalloc(out_size);
+  memset(out, 0, out_size);
+  out->reply_id = ULS_XML_DATA;
+  out->info_len = xml_size;
+  memcpy(out->data, xml_ptr, xml_size + 1);
+  xfree(xml_ptr); xml_ptr = 0;
+  enqueue_reply_to_client(p, out_size, out);
+  info("%s -> OK, size = %zu", logbuf, xml_size); 
+  xfree(out); out = 0;
+  bitset_free(&marked);
+}
+
+static void
+cmd_get_group_info(
+        struct client_state *p,
+        int pkt_len,
+        struct userlist_pk_map_contest *data)
+{
+  unsigned char logbuf[1024];
+  const struct userlist_group *grp;
+  char *xml_ptr = 0;
+  size_t xml_size = 0;
+  FILE *fout = 0;
+  struct userlist_pk_xml_data *out = 0;
+  size_t out_size;
+
+  snprintf(logbuf, sizeof(logbuf), "GET_GROUP_INFO: %d, %d",
+           p->user_id, data->contest_id);
+
+  if (is_admin(p, logbuf) < 0) return;
+
+  grp = plugin_call(get_group, data->contest_id);
+  if (!grp) {
+    err("%s -> invalid group %d", logbuf, data->contest_id);
+    send_reply(p, -ULS_ERR_BAD_GROUP_ID);
+    return;
+  }
+
+  fout = open_memstream(&xml_ptr, &xml_size);
+  userlist_write_xml_header(fout);
+  userlist_write_groups_header(fout);
+  userlist_unparse_usergroup(fout, grp, "      ", "\n");
+  userlist_write_groups_footer(fout);
+  userlist_write_xml_footer(fout);
+  fclose(fout); fout = 0;
+
+  out_size = sizeof(*out) + xml_size;
+  out = alloca(out_size);
+  memset(out, 0, out_size);
+  out->reply_id = ULS_XML_DATA;
+  out->info_len = xml_size;
+  memcpy(out->data, xml_ptr, xml_size + 1);
+  xfree(xml_ptr); xml_ptr = 0;
+  enqueue_reply_to_client(p, out_size, out);
+  info("%s -> OK, size = %zu", logbuf, xml_size); 
+}
+
 static void (*cmd_table[])() =
 {
   [ULS_REGISTER_NEW] =          cmd_register_new,
@@ -9044,6 +10083,20 @@ static void (*cmd_table[])() =
   [ULS_CREATE_GROUP_MEMBER] =   cmd_create_group_member,
   [ULS_DELETE_GROUP_MEMBER] =   cmd_delete_group_member,
   [ULS_GET_GROUPS] =            cmd_get_groups,
+  [ULS_LIST_ALL_USERS_2] =      cmd_list_all_users_2,
+  [ULS_GET_USER_COUNT] =        cmd_get_user_count,
+  [ULS_LIST_ALL_GROUPS_2] =     cmd_list_all_groups_2,
+  [ULS_GET_GROUP_COUNT] =       cmd_get_group_count,
+  [ULS_PRIV_SET_REG_PASSWD_PLAIN] = cmd_priv_set_passwd_2,
+  [ULS_PRIV_SET_REG_PASSWD_SHA1] = cmd_priv_set_passwd_2,
+  [ULS_PRIV_SET_CNTS_PASSWD_PLAIN] = cmd_priv_set_passwd_2,
+  [ULS_PRIV_SET_CNTS_PASSWD_SHA1] = cmd_priv_set_passwd_2,
+  [ULS_CREATE_USER_2] =         cmd_create_user_2,
+  [ULS_PREV_USER] =             cmd_next_user,
+  [ULS_NEXT_USER] =             cmd_next_user,
+  [ULS_LIST_ALL_USERS_3] =      cmd_list_all_users_3,
+  [ULS_LIST_ALL_USERS_4] =      cmd_list_all_users_4,
+  [ULS_GET_GROUP_INFO] =        cmd_get_group_info,
 
   [ULS_LAST_CMD] 0
 };
@@ -9134,6 +10187,20 @@ static int (*check_table[])() =
   [ULS_CREATE_GROUP_MEMBER] =   check_pk_register_contest,
   [ULS_DELETE_GROUP_MEMBER] =   check_pk_register_contest,
   [ULS_GET_GROUPS] =            check_pk_set_user_info,
+  [ULS_LIST_ALL_USERS_2] =      check_pk_list_users_2,
+  [ULS_GET_USER_COUNT] =        check_pk_list_users_2,
+  [ULS_LIST_ALL_GROUPS_2] =     check_pk_list_users_2,
+  [ULS_GET_GROUP_COUNT] =       check_pk_list_users_2,
+  [ULS_PRIV_SET_REG_PASSWD_PLAIN] = check_pk_set_password,
+  [ULS_PRIV_SET_REG_PASSWD_SHA1] = check_pk_set_password,
+  [ULS_PRIV_SET_CNTS_PASSWD_PLAIN] = check_pk_set_password,
+  [ULS_PRIV_SET_CNTS_PASSWD_SHA1] = check_pk_set_password,
+  [ULS_CREATE_USER_2] =         check_pk_create_user_2,
+  [ULS_PREV_USER] =             check_pk_list_users_2,
+  [ULS_NEXT_USER] =             check_pk_list_users_2,
+  [ULS_LIST_ALL_USERS_3] =      check_pk_list_users_2,
+  [ULS_LIST_ALL_USERS_4] =      check_pk_list_users_2,
+  [ULS_GET_GROUP_INFO] =        check_pk_map_contest,
 
   [ULS_LAST_CMD] 0
 };
