+
+/* Send packet to clients that are known to be operators. If server
+ is router and `route' is TRUE then the packet would go to all operators
+ in the SILC network. If `route' is FALSE then only local operators
+ (local for server and cell wide for router). If `local' is TRUE then
+ only locally connected operators receive the packet. If `local' is
+ TRUE then `route' is ignored. If server is normal server and `route'
+ is FALSE it is equivalent to `local' being TRUE. */
+
+void silc_server_send_opers(SilcServer server,
+ SilcPacketType type,
+ SilcPacketFlags flags,
+ SilcBool route, bool local,
+ unsigned char *data,
+ SilcUInt32 data_len)
+{
+ SilcList list;
+ SilcIDCacheEntry id_cache = NULL;
+ SilcClientEntry client = NULL;
+ SilcIDListData idata;
+ SilcPacketStream sock;
+ SilcServerEntry *routed = NULL;
+ SilcUInt32 routed_count = 0;
+ SilcBool gone = FALSE;
+ int k;
+
+ SILC_LOG_DEBUG(("Sending %s packet to operators",
+ silc_get_packet_name(type)));
+
+ /* If local was requested send only locally connected operators. */
+ if (local || (server->server_type == SILC_SERVER && !route)) {
+ if (!silc_idcache_get_all(server->local_list->clients, &list))
+ return;
+ silc_list_start(list);
+ while ((id_cache = silc_list_get(list))) {
+ client = (SilcClientEntry)id_cache->context;
+ if (!client->router && SILC_IS_LOCAL(client) &&
+ (client->mode & SILC_UMODE_SERVER_OPERATOR ||
+ client->mode & SILC_UMODE_ROUTER_OPERATOR)) {
+
+ /* Send the packet to locally connected operator */
+ silc_server_packet_send_dest(server, client->connection, type, flags,
+ client->id, SILC_ID_CLIENT,
+ data, data_len);
+ }
+ }
+ return;
+ }
+
+ if (!silc_idcache_get_all(server->local_list->clients, &list))
+ return;
+ silc_list_start(list);
+ while ((id_cache = silc_list_get(list))) {
+ client = (SilcClientEntry)id_cache->context;
+ if (!(client->mode & SILC_UMODE_SERVER_OPERATOR) &&
+ !(client->mode & SILC_UMODE_ROUTER_OPERATOR))
+ continue;
+
+ if (server->server_type != SILC_SERVER && client->router &&
+ ((!route && client->router->router == server->id_entry) || route)) {
+
+ /* Check if we have sent the packet to this route already */
+ for (k = 0; k < routed_count; k++)
+ if (routed[k] == client->router)
+ break;
+ if (k < routed_count)
+ continue;
+
+ /* Route only once to router */
+ sock = client->router->connection;
+ idata = silc_packet_get_context(sock);
+ if (idata->conn_type == SILC_CONN_ROUTER) {
+ if (gone)
+ continue;
+ gone = TRUE;
+ }
+
+ /* Send the packet */
+ silc_server_packet_send_dest(server, sock, type, flags,
+ client->id, SILC_ID_CLIENT,
+ data, data_len);
+
+ /* Mark this route routed already */
+ routed = silc_realloc(routed, sizeof(*routed) * (routed_count + 1));
+ routed[routed_count++] = client->router;
+ continue;
+ }
+
+ if (client->router || !client->connection)
+ continue;
+
+ /* Send to locally connected client */
+ sock = client->connection;
+ silc_server_packet_send_dest(server, sock, type, flags,
+ client->id, SILC_ID_CLIENT,
+ data, data_len);
+
+ }
+
+ if (!silc_idcache_get_all(server->global_list->clients, &list))
+ return;
+ silc_list_start(list);
+ while ((id_cache = silc_list_get(list))) {
+ client = (SilcClientEntry)id_cache->context;
+ if (!(client->mode & SILC_UMODE_SERVER_OPERATOR) &&
+ !(client->mode & SILC_UMODE_ROUTER_OPERATOR))
+ continue;
+
+ if (server->server_type != SILC_SERVER && client->router &&
+ ((!route && client->router->router == server->id_entry) || route)) {
+
+ /* Check if we have sent the packet to this route already */
+ for (k = 0; k < routed_count; k++)
+ if (routed[k] == client->router)
+ break;
+ if (k < routed_count)
+ continue;
+
+ /* Route only once to router */
+ sock = client->router->connection;
+ idata = silc_packet_get_context(sock);
+ if (idata->conn_type == SILC_CONN_ROUTER) {
+ if (gone)
+ continue;
+ gone = TRUE;
+ }
+
+ /* Send the packet */
+ silc_server_packet_send_dest(server, sock, type, flags,
+ client->id, SILC_ID_CLIENT,
+ data, data_len);
+
+ /* Mark this route routed already */
+ routed = silc_realloc(routed, sizeof(*routed) * (routed_count + 1));
+ routed[routed_count++] = client->router;
+ continue;
+ }
+
+ if (client->router || !client->connection)
+ continue;
+
+ /* Send to locally connected client */
+ sock = client->connection;
+ silc_server_packet_send_dest(server, sock, type, flags,
+ client->id, SILC_ID_CLIENT,
+ data, data_len);
+ }
+ silc_free(routed);
+}
+
+/* Send a notify packet to operators */
+
+void silc_server_send_opers_notify(SilcServer server,
+ SilcBool route,
+ SilcBool local,
+ SilcNotifyType type,
+ SilcUInt32 argc, ...)
+{
+ va_list ap;
+ SilcBuffer packet;
+
+ va_start(ap, argc);
+ packet = silc_notify_payload_encode(type, argc, ap);
+ if (packet)
+ silc_server_send_opers(server, SILC_PACKET_NOTIFY, 0,
+ route, local, packet->data, silc_buffer_len(packet));
+ silc_buffer_free(packet);
+ va_end(ap);
+}