예제 #1
0
SilcBool silc_client_attribute_del(SilcClient client,
				   SilcClientConnection conn,
				   SilcAttribute attribute,
				   SilcAttributePayload attr)
{
  SilcBool ret;

  if (!conn->internal->attrs)
    return FALSE;

  if (attr) {
    attribute = silc_attribute_get_attribute(attr);
    ret = silc_hash_table_del_by_context(conn->internal->attrs,
					 SILC_32_TO_PTR(attribute), attr);
  } else if (attribute) {
    silc_hash_table_find_foreach(conn->internal->attrs,
				 SILC_32_TO_PTR(attribute),
				 silc_client_attribute_del_foreach, conn);
    ret = TRUE;
  } else{
    return FALSE;
  }

  if (ret)
    if (!silc_hash_table_count(conn->internal->attrs)) {
      silc_hash_table_free(conn->internal->attrs);
      conn->internal->attrs = NULL;
    }

  return ret;
}
예제 #2
0
SilcBool silc_client_remove_from_channel(SilcClient client,
					 SilcClientConnection conn,
					 SilcChannelEntry channel,
					 SilcClientEntry client_entry)
{
  SilcChannelUser chu;

  chu = silc_client_on_channel(channel, client_entry);
  if (!chu)
    return FALSE;

  SILC_LOG_DEBUG(("Remove client %s from channel", client_entry->nickname));

  silc_rwlock_wrlock(client_entry->internal.lock);
  silc_rwlock_wrlock(channel->internal.lock);

  silc_hash_table_del(chu->client->channels, chu->channel);
  silc_hash_table_del(chu->channel->user_list, chu->client);
  silc_free(chu);

  /* If channel became empty, delete it */
  if (!silc_hash_table_count(channel->user_list))
    silc_client_del_channel(client, conn, channel);

  silc_rwlock_unlock(client_entry->internal.lock);
  silc_rwlock_unlock(channel->internal.lock);

  silc_client_unref_client(client, conn, client_entry);
  silc_client_unref_channel(client, conn, channel);

  return TRUE;
}
예제 #3
0
파일: idlist.c 프로젝트: FabrizioFabbe/silc
void silc_idlist_client_destructor(SilcIDCache cache,
				   SilcIDCacheEntry entry,
				   void *dest_context,
				   void *app_context)
{
  SilcServer server = dest_context;
  SilcClientEntry client;

  client = (SilcClientEntry)entry->context;
  if (client) {
    /* Remove client's public key from repository, this will free it too. */
    if (client->data.public_key)
      silc_skr_del_public_key(server->repository, client->data.public_key,
			      client);

    assert(!silc_hash_table_count(client->channels));
    silc_free(client->nickname);
    silc_free(client->servername);
    silc_free(client->username);
    silc_free(client->userinfo);
    silc_free(client->id);
    silc_free(client->attrs);
    silc_hash_table_free(client->channels);

    memset(client, 'A', sizeof(*client));
    silc_free(client);
  }
}
예제 #4
0
void silc_client_remove_from_channels(SilcClient client,
				      SilcClientConnection conn,
				      SilcClientEntry client_entry)
{
  SilcHashTableList htl;
  SilcChannelUser chu;

  if (!silc_hash_table_count(client_entry->channels))
    return;

  SILC_LOG_DEBUG(("Remove client from all joined channels"));

  silc_rwlock_wrlock(client_entry->internal.lock);

  silc_hash_table_list(client_entry->channels, &htl);
  while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
    silc_rwlock_wrlock(chu->channel->internal.lock);

    silc_hash_table_del(chu->client->channels, chu->channel);
    silc_hash_table_del(chu->channel->user_list, chu->client);

    /* If channel became empty, delete it */
    if (!silc_hash_table_count(chu->channel->user_list))
      silc_client_del_channel(client, conn, chu->channel);

    silc_rwlock_unlock(chu->channel->internal.lock);

    silc_client_unref_client(client, conn, chu->client);
    silc_client_unref_channel(client, conn, chu->channel);
    silc_free(chu);
  }

  silc_rwlock_unlock(client_entry->internal.lock);

  silc_hash_table_list_reset(&htl);
}
예제 #5
0
파일: chat.c 프로젝트: Lilitana/Pidgin
static void
silcpurple_chat_getinfo(PurpleConnection *gc, GHashTable *components)
{
	SilcPurple sg = gc->proto_data;
	const char *chname;
	char tmp[256], *tmp2;
	GString *s;
	SilcChannelEntry channel;
	SilcHashTableList htl;
	SilcChannelUser chu;

	if (!components)
		return;

	chname = g_hash_table_lookup(components, "channel");
	if (!chname)
		return;
	channel = silc_client_get_channel(sg->client, sg->conn,
					  (char *)chname);
	if (!channel) {
		silc_client_get_channel_resolve(sg->client, sg->conn,
						(char *)chname,
						silcpurple_chat_getinfo_res,
						components);
		return;
	}

	s = g_string_new("");
	tmp2 = g_markup_escape_text(channel->channel_name, -1);
	g_string_append_printf(s, _("<b>Channel Name:</b> %s"), tmp2);
	g_free(tmp2);
	if (channel->user_list && silc_hash_table_count(channel->user_list))
		g_string_append_printf(s, _("<br><b>User Count:</b> %d"),
				       (int)silc_hash_table_count(channel->user_list));

	silc_hash_table_list(channel->user_list, &htl);
	while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
		if (chu->mode & SILC_CHANNEL_UMODE_CHANFO) {
			tmp2 = g_markup_escape_text(chu->client->nickname, -1);
			g_string_append_printf(s, _("<br><b>Channel Founder:</b> %s"),
					       tmp2);
			g_free(tmp2);
			break;
		}
	}
	silc_hash_table_list_reset(&htl);

	if (channel->cipher)
		g_string_append_printf(s, _("<br><b>Channel Cipher:</b> %s"),
				       channel->cipher);

	if (channel->hmac)
		/* Definition of HMAC: http://en.wikipedia.org/wiki/HMAC */
		g_string_append_printf(s, _("<br><b>Channel HMAC:</b> %s"),
				       channel->hmac);

	if (channel->topic) {
		tmp2 = g_markup_escape_text(channel->topic, -1);
		g_string_append_printf(s, _("<br><b>Channel Topic:</b><br>%s"), tmp2);
		g_free(tmp2);
	}

	if (channel->mode) {
		g_string_append_printf(s, _("<br><b>Channel Modes:</b> "));
		silcpurple_get_chmode_string(channel->mode, tmp, sizeof(tmp));
		g_string_append(s, tmp);
	}

	if (channel->founder_key) {
		char *fingerprint, *babbleprint;
		unsigned char *pk;
		SilcUInt32 pk_len;
		pk = silc_pkcs_public_key_encode(channel->founder_key, &pk_len);
		if (pk) {
			fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
			babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);

			g_string_append_printf(s, _("<br><b>Founder Key Fingerprint:</b><br>%s"), fingerprint);
			g_string_append_printf(s, _("<br><b>Founder Key Babbleprint:</b><br>%s"), babbleprint);

			silc_free(fingerprint);
			silc_free(babbleprint);
			silc_free(pk);
		}
	}

	purple_notify_formatted(gc, NULL, _("Channel Information"), NULL, s->str, NULL, NULL);
	g_string_free(s, TRUE);
}
예제 #6
0
SilcMime silc_mime_assemble(SilcMimeAssembler assembler, SilcMime partial)
{
    char *type, *id = NULL, *tmp;
    SilcHashTable f;
    SilcMime p, complete;
    int i, number, total = -1;
    const unsigned char *data;
    SilcUInt32 data_len;
    SilcBuffer compbuf = NULL;

    SILC_LOG_DEBUG(("Assembling MIME fragments"));

    if (!assembler || !partial)
        goto err;

    type = (char *)silc_mime_get_field(partial, "Content-Type");
    if (!type)
        goto err;

    /* Get ID */
    tmp = strstr(type, "id=");
    if (!tmp)
        goto err;
    if (strlen(tmp) <= 4)
        goto err;
    tmp += 3;
    if (*tmp == '"')
        tmp++;
    id = strdup(tmp);
    if (strchr(id, ';'))
        *strchr(id, ';') = '\0';
    if (strrchr(id, '"'))
        *strrchr(id, '"') = '\0';

    SILC_LOG_DEBUG(("Fragment ID %s", id));

    /* Get fragment number */
    tmp = strstr(type, "number=");
    if (!tmp)
        goto err;
    tmp = strchr(tmp, '=');
    if (strlen(tmp) < 2)
        goto err;
    tmp++;
    if (strchr(tmp, ';')) {
        tmp = strdup(tmp);
        *strchr(tmp, ';') = '\0';
        number = atoi(tmp);
        silc_free(tmp);
    } else {
        number = atoi(tmp);
    }

    SILC_LOG_DEBUG(("Fragment number %d", number));

    /* Find fragments with this ID. */
    if (!silc_hash_table_find(assembler->fragments, (void *)id,
                              NULL, (void *)&f)) {
        /* This is new fragment to new message.  Add to hash table and return. */
        f = silc_hash_table_alloc(0, silc_hash_uint, NULL, NULL, NULL,
                                  silc_mime_assemble_dest, NULL, TRUE);
        if (!f)
            goto err;
        silc_hash_table_add(f, SILC_32_TO_PTR(number), partial);
        silc_hash_table_add(assembler->fragments, id, f);
        return NULL;
    }

    /* Try to get total number */
    tmp = strstr(type, "total=");
    if (tmp) {
        tmp = strchr(tmp, '=');
        if (strlen(tmp) < 2)
            goto err;
        tmp++;
        if (strchr(tmp, ';')) {
            tmp = strdup(tmp);
            *strchr(tmp, ';') = '\0';
            total = atoi(tmp);
            silc_free(tmp);
        } else {
            total = atoi(tmp);
        }

        SILC_LOG_DEBUG(("Fragment total %d", total));
    }

    /* If more fragments to come, add to hash table */
    if (number != total) {
        silc_hash_table_add(f, SILC_32_TO_PTR(number), partial);
        return NULL;
    }

    silc_hash_table_add(f, SILC_32_TO_PTR(number), partial);

    /* Verify that we really have all the fragments */
    if (silc_hash_table_count(f) < total)
        return NULL;

    /* Assemble the complete MIME message now. We get them in order from
       the hash table. */
    for (i = 1; i <= total; i++) {
        if (!silc_hash_table_find(f, SILC_32_TO_PTR(i), NULL, (void *)&p))
            goto err;

        /* The fragment is in the data portion of the partial message */
        data = silc_mime_get_data(p, &data_len);
        if (!data)
            goto err;

        /* Assemble */
        if (!compbuf) {
            compbuf = silc_buffer_alloc_size(data_len);
            if (!compbuf)
                goto err;
            silc_buffer_put(compbuf, data, data_len);
        } else {
            compbuf = silc_buffer_realloc(compbuf, silc_buffer_truelen(compbuf) +
                                          data_len);
            if (!compbuf)
                goto err;
            silc_buffer_put_tail(compbuf, data, data_len);
            silc_buffer_pull_tail(compbuf, data_len);
        }
    }

    /* Now parse the complete MIME message and deliver it */
    complete = silc_mime_decode(NULL, (const unsigned char *)compbuf->head,
                                silc_buffer_truelen(compbuf));
    if (!complete)
        goto err;

    /* Delete the hash table entry. Destructors will free memory */
    silc_hash_table_del(assembler->fragments, (void *)id);
    silc_free(id);
    silc_buffer_free(compbuf);

    return complete;

err:
    silc_free(id);
    if (compbuf)
        silc_buffer_free(compbuf);
    silc_mime_free(partial);
    return NULL;
}
예제 #7
0
unsigned char *silc_mime_encode(SilcMime mime, SilcUInt32 *encoded_len)
{
    SilcMime part;
    SilcHashTableList htl;
    SilcBufferStruct buf;
    SilcBuffer buffer;
    char *field, *value, tmp[1024], tmp2[4];
    unsigned char *ret;
    int i;

    SILC_LOG_DEBUG(("Encoding MIME message"));

    if (!mime)
        return NULL;

    memset(&buf, 0, sizeof(buf));

    /* Encode the headers. Order doesn't matter */
    i = 0;
    silc_hash_table_list(mime->fields, &htl);
    while (silc_hash_table_get(&htl, (void *)&field, (void *)&value)) {
        memset(tmp, 0, sizeof(tmp));
        SILC_LOG_DEBUG(("Header %s: %s", field, value));
        silc_snprintf(tmp, sizeof(tmp) - 1, "%s: %s\r\n", field, value);
        silc_buffer_strformat(&buf, tmp, SILC_STRFMT_END);
        i++;
    }
    silc_hash_table_list_reset(&htl);
    if (i)
        silc_buffer_strformat(&buf, "\r\n", SILC_STRFMT_END);

    /* Assemble the whole buffer */
    buffer = silc_buffer_alloc_size(mime->data_len + silc_buffer_len(&buf));
    if (!buffer)
        return NULL;

    /* Add headers */
    if (silc_buffer_len(&buf)) {
        silc_buffer_put(buffer, buf.head, silc_buffer_len(&buf));
        silc_buffer_pull(buffer, silc_buffer_len(&buf));
        silc_buffer_purge(&buf);
    }

    /* Add data */
    if (mime->data) {
        SILC_LOG_DEBUG(("Data len %d", mime->data_len));
        silc_buffer_put(buffer, mime->data, mime->data_len);
    }

    /* Add multiparts */
    if (mime->multiparts) {
        SILC_LOG_DEBUG(("Encoding multiparts"));

        silc_dlist_start(mime->multiparts);
        i = 0;
        while ((part = silc_dlist_get(mime->multiparts)) != SILC_LIST_END) {
            unsigned char *pd;
            SilcUInt32 pd_len;

            /* Recursive encoding */
            pd = silc_mime_encode(part, &pd_len);
            if (!pd)
                return NULL;

            memset(tmp, 0, sizeof(tmp));
            memset(tmp2, 0, sizeof(tmp2));

            /* If fields are not present, add extra CRLF */
            if (!silc_hash_table_count(part->fields))
                silc_snprintf(tmp2, sizeof(tmp2) - 1, "\r\n");
            silc_snprintf(tmp, sizeof(tmp) - 1, "%s--%s\r\n%s",
                          i != 0 ? "\r\n" : "", mime->boundary, tmp2);
            i = 1;

            buffer = silc_buffer_realloc(buffer, silc_buffer_truelen(buffer) +
                                         pd_len + strlen(tmp));
            if (!buffer)
                return NULL;
            silc_buffer_put_tail(buffer, tmp, strlen(tmp));
            silc_buffer_pull_tail(buffer, strlen(tmp));
            silc_buffer_put_tail(buffer, pd, pd_len);
            silc_buffer_pull_tail(buffer, pd_len);
            silc_free(pd);
        }

        memset(tmp, 0, sizeof(tmp));
        silc_snprintf(tmp, sizeof(tmp) - 1, "\r\n--%s--\r\n", mime->boundary);
        buffer = silc_buffer_realloc(buffer, silc_buffer_truelen(buffer) +
                                     strlen(tmp));
        if (!buffer)
            return NULL;
        silc_buffer_put_tail(buffer, tmp, strlen(tmp));
        silc_buffer_pull_tail(buffer, strlen(tmp));
    }

    ret = silc_buffer_steal(buffer, encoded_len);
    silc_buffer_free(buffer);

    return ret;
}
예제 #8
0
SilcMime silc_mime_decode(SilcMime mime, const unsigned char *data,
                          SilcUInt32 data_len)
{
    SilcMime m = NULL;
    int i, k;
    char *tmp, *field, *value, *line;

    SILC_LOG_DEBUG(("Parsing MIME message"));

    if (!data)
        return NULL;

    if (!mime) {
        mime = silc_mime_alloc();
        if (!mime)
            return NULL;
        m = mime;
    }

    /* Parse the fields */
    line = tmp = (char *)data;
    for (i = 0; i < data_len; i++) {
        /* Get field line */
        if (data_len - i >= 2 && tmp[i] == '\r' && tmp[i + 1] == '\n') {
            /* Get field */
            field = strchr(line, ':');
            if (!field)
                goto err;
            field = silc_memdup(line, field - line);
            if (!field)
                goto err;

            /* Get value. Remove whitespaces too. */
            value = strchr(line, ':');
            if ((tmp + i) - value < 2)
                goto err;
            value++;
            for (k = 0; k < (tmp + i) - value; k++) {
                if (value[k] == '\r')
                    goto err;
                if (value[k] != ' ' && value[k] != '\t')
                    break;
            }
            value += k;
            if ((tmp + i) - value < 1)
                goto err;
            value = silc_memdup(value, (tmp + i) - value);
            if (!value)
                goto err;

            SILC_LOG_DEBUG(("Header '%s' '%s'", field, value));

            /* Add field and value */
            silc_mime_add_field(mime, field, value);
            silc_free(field);
            silc_free(value);

            /* Mark start of next line */
            line = (tmp + i) + 2;
            i += 2;

            /* Break if this is last header */
            if (data_len - i >= 2 &&
                    tmp[i] == '\r' && tmp[i + 1] == '\n') {
                i += 2;
                break;
            }
        }
    }

    /* Parse multiparts if present */
    field = (char *)silc_mime_get_field(mime, "Content-Type");
    if (field && strstr(field, "multipart")) {
        char b[1024];
        SilcMime p;
        unsigned int len;

        mime->multiparts = silc_dlist_init();
        if (!mime->multiparts)
            goto err;

        /* Get multipart type */
        value = strchr(field, '/');
        if (!value)
            goto err;
        value++;
        if (strchr(field, '"'))
            value++;
        if (!strchr(field, ';'))
            goto err;
        memset(b, 0, sizeof(b));
        len = (unsigned int)(strchr(field, ';') - value);
        if (len > sizeof(b) - 1)
            goto err;
        strncpy(b, value, len);
        if (strchr(b, '"'))
            *strchr(b, '"') = '\0';
        mime->multitype = silc_memdup(b, strlen(b));

        /* Get boundary */
        value = strrchr(field, '=');
        if (value && strlen(value) > 1) {
            value++;

            SILC_LOG_DEBUG(("Boundary '%s'", value));

            memset(b, 0, sizeof(b));
            line = strdup(value);
            if (strrchr(line, '"')) {
                *strrchr(line, '"') = '\0';
                silc_snprintf(b, sizeof(b) - 1, "--%s", line + 1);
                mime->boundary = strdup(line + 1);
            } else {
                silc_snprintf(b, sizeof(b) - 1, "--%s", line);
                mime->boundary = strdup(line);
            }
            silc_free(line);

            for (i = i; i < data_len; i++) {
                /* Get boundary data */
                if (data_len - i >= strlen(b) &&
                        tmp[i] == '-' && tmp[i + 1] == '-') {
                    if (memcmp(tmp + i, b, strlen(b)))
                        continue;

                    i += strlen(b);

                    if (data_len - i >= 4 &&
                            tmp[i    ] == '\r' && tmp[i + 1] == '\n' &&
                            tmp[i + 2] == '\r' && tmp[i + 3] == '\n')
                        i += 4;
                    else if (data_len - i >= 2 &&
                             tmp[i] == '\r' && tmp[i + 1] == '\n')
                        i += 2;
                    else if (data_len - i >= 2 &&
                             tmp[i] == '-' && tmp[i + 1] == '-')
                        break;

                    line = tmp + i;

                    /* Find end of boundary */
                    for (k = i; k < data_len; k++)
                        if (data_len - k >= strlen(b) &&
                                tmp[k] == '-' && tmp[k + 1] == '-')
                            if (!memcmp(tmp + k, b, strlen(b)))
                                break;
                    if (k >= data_len)
                        goto err;

                    /* Remove preceding CRLF */
                    k -= 2;

                    /* Parse the part */
                    p = silc_mime_decode(NULL, line, k - i);
                    if (!p)
                        goto err;

                    silc_dlist_add(mime->multiparts, p);
                    i += (k - i);
                }
            }
        }
    } else {
        /* Get data area.  If we are at the end and we have fields present
           there is no data area present, but, if fields are not present we
           only have data area. */
        if (i >= data_len && !silc_hash_table_count(mime->fields))
            i = 0;
        SILC_LOG_DEBUG(("Data len %d", data_len - i));
        if (data_len - i)
            silc_mime_add_data(mime, tmp + i, data_len - i);
    }

    return mime;

err:
    if (m)
        silc_mime_free(m);
    return NULL;
}
예제 #9
0
static char
silc_server_command_reply_whowas_save(SilcServerCommandReplyContext cmd)
{
  SilcServer server = cmd->server;
  SilcUInt32 len, id_len;
  unsigned char *id_data;
  char *nickname, *username, *realname;
  SilcID id;
  SilcClientEntry client;
  SilcIDCacheEntry cache = NULL;
  char nick[128 + 1], servername[256 + 1], uname[128 + 1];
  int global = FALSE;
  const char *hostname, *ip;

  silc_socket_stream_get_info(silc_packet_stream_get_stream(cmd->sock),
			      NULL, &hostname, &ip, NULL);

  id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
  nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
  username = silc_argument_get_arg_type(cmd->args, 4, &len);
  if (!id_data || !nickname || !username)
    return FALSE;

  realname = silc_argument_get_arg_type(cmd->args, 5, &len);

  if (!silc_id_payload_parse_id(id_data, id_len, &id))
    return FALSE;

  /* Check if we have this client cached already. */

  client = silc_idlist_find_client_by_id(server->local_list,
					 SILC_ID_GET_ID(id),
					 FALSE, &cache);
  if (!client) {
    client = silc_idlist_find_client_by_id(server->global_list,
					   SILC_ID_GET_ID(id),
					   FALSE, &cache);
    global = TRUE;
  }

  if (!client) {
    /* If router did not find such Client ID in its lists then this must
       be bogus client or some router in the net is buggy. */
    if (server->server_type != SILC_SERVER)
      return FALSE;

    /* Take hostname out of nick string if it includes it. */
    silc_parse_userfqdn(nickname, nick, sizeof(nick), servername,
			sizeof(servername));

    /* We don't have that client anywhere, add it. The client is added
       to global list since server didn't have it in the lists so it must be
       global. */
    client = silc_idlist_add_client(server->global_list,
				    strdup(nick), username,
				    strdup(realname),
				    silc_id_dup(SILC_ID_GET_ID(id),
						SILC_ID_CLIENT),
				    silc_packet_get_context(cmd->sock), NULL);
    if (!client) {
      SILC_LOG_ERROR(("Could not add new client to the ID Cache"));
      return FALSE;
    }

    client->data.status |= SILC_IDLIST_STATUS_RESOLVED;
    client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING;
    client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
    client->servername = servername[0] ? strdup(servername) : NULL;
  } else {
    /* We have the client already, update the data */

    /* Check nickname */
    silc_parse_userfqdn(nickname, nick, sizeof(nick), servername,
			sizeof(servername));
    nickname = silc_identifier_check(nick, strlen(nick), SILC_STRING_UTF8,
				     128, NULL);
    if (!nickname) {
      SILC_LOG_ERROR(("Malformed nickname '%s' received in WHOWAS reply "
		      "from %s",
		      nick, hostname ? hostname : ""));
      return FALSE;
    }

    /* Check username */
    silc_parse_userfqdn(username, uname, sizeof(uname), NULL, 0);
    if (!silc_identifier_verify(uname, strlen(uname), SILC_STRING_UTF8, 128))
      return FALSE;

    silc_free(client->nickname);
    silc_free(client->username);
    silc_free(client->servername);

    client->nickname = strdup(nick);
    client->username = strdup(username);
    client->servername = servername[0] ? strdup(servername) : NULL;
    client->data.status |= SILC_IDLIST_STATUS_RESOLVED;
    client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING;
    client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;

    /* Update cache entry */
    silc_idcache_update_by_context(global ? server->global_list->clients :
				   server->local_list->clients, client, NULL,
				   nickname, TRUE);
  }

  /* If client is global and is not on any channel then add that we'll
     expire the entry after a while. */
  if (global) {
    client = silc_idlist_find_client_by_id(server->global_list, client->id,
					   FALSE, &cache);
    if (client && !silc_hash_table_count(client->channels)) {
      client->data.created = silc_time();
      silc_dlist_del(server->expired_clients, client);
      silc_dlist_add(server->expired_clients, client);
    }
  }

  return TRUE;
}