+ SilcPacket packet; /* Notify packet */
+ SilcNotifyPayload payload; /* Parsed notify payload */
+ SilcFSMThread fsm; /* Notify FSM thread */
+ SilcChannelEntry channel; /* Channel entry being resolved */
+ SilcClientEntry client_entry; /* Client entry being resolved */
+ SilcUInt32 resolve_retry; /* Resolving retry counter */
+} *SilcClientNotify;
+
+/************************ Static utility functions **************************/
+
+/* The client entires in notify processing are resolved if they do not exist,
+ or they are not valid. We go to this callback after resolving where we
+ check if the client entry has become valid. If resolving succeeded the
+ entry is valid but remains invalid if resolving failed. This callback
+ will continue processing the notify. We use this callback also with other
+ entry resolving. */
+
+static void silc_client_notify_resolved(SilcClient client,
+ SilcClientConnection conn,
+ SilcStatus status,
+ SilcDList entries,
+ void *context)
+{
+ SilcClientNotify notify = context;
+
+ /* If entry is still invalid, resolving failed. Finish notify processing. */
+ if (notify->client_entry && !notify->client_entry->internal.valid) {
+ /* If resolving timedout try it again many times. */
+ if (status != SILC_STATUS_ERR_TIMEDOUT || ++notify->resolve_retry > 1000) {
+ silc_fsm_next(notify->fsm, silc_client_notify_processed);
+
+ /* Unref client only in case of non-timeout error. In case of timeout
+ occurred, the routine reprocessing the notify is expected not to
+ create new references of the entry. */
+ silc_client_unref_client(client, conn, notify->client_entry);
+ }
+ }
+
+ /* If no entries found, just finish the notify processing */
+ if (!entries && !notify->client_entry)
+ silc_fsm_next(notify->fsm, silc_client_notify_processed);
+
+ if (notify->channel) {
+ notify->channel->internal.resolve_cmd_ident = 0;
+ silc_client_unref_channel(client, conn, notify->channel);
+ }
+
+ /* Continue processing the notify */
+ SILC_FSM_CALL_CONTINUE_SYNC(notify->fsm);
+}
+
+/* Continue notify processing after it was suspended while waiting for
+ channel information being resolved. */
+
+static SilcBool silc_client_notify_wait_continue(SilcClient client,
+ SilcClientConnection conn,
+ SilcCommand command,
+ SilcStatus status,
+ SilcStatus error,
+ void *context,
+ va_list ap)
+{
+ SilcClientNotify notify = context;
+
+ /* Continue after last command reply received */
+ if (SILC_STATUS_IS_ERROR(status) || status == SILC_STATUS_OK ||
+ status == SILC_STATUS_LIST_END)
+ SILC_FSM_CALL_CONTINUE_SYNC(notify->fsm);
+
+ return TRUE;
+}
+
+/********************************* Notify ***********************************/
+
+/* Process received notify packet */
+
+SILC_FSM_STATE(silc_client_notify)
+{
+ SilcPacket packet = state_context;
+ SilcClientNotify notify;
+ SilcNotifyPayload payload;
+
+ payload = silc_notify_payload_parse(silc_buffer_data(&packet->buffer),
+ silc_buffer_len(&packet->buffer));
+ if (!payload) {
+ SILC_LOG_DEBUG(("Malformed notify payload"));
+ silc_packet_free(packet);
+ return SILC_FSM_FINISH;
+ }
+
+ if (!silc_notify_get_args(payload)) {
+ SILC_LOG_DEBUG(("Malformed notify %d", silc_notify_get_type(payload)));
+ silc_notify_payload_free(payload);
+ silc_packet_free(packet);
+ return SILC_FSM_FINISH;
+ }
+
+ notify = silc_calloc(1, sizeof(*notify));
+ if (!notify) {
+ silc_notify_payload_free(payload);
+ silc_packet_free(packet);
+ return SILC_FSM_FINISH;
+ }
+
+ notify->packet = packet;
+ notify->payload = payload;
+ notify->fsm = fsm;
+ silc_fsm_set_state_context(fsm, notify);
+
+ /* Process the notify */
+ switch (silc_notify_get_type(payload)) {
+
+ case SILC_NOTIFY_TYPE_NONE:
+ /** NONE */
+ silc_fsm_next(fsm, silc_client_notify_none);
+ break;
+
+ case SILC_NOTIFY_TYPE_INVITE:
+ /** INVITE */
+ silc_fsm_next(fsm, silc_client_notify_invite);
+ break;
+
+ case SILC_NOTIFY_TYPE_JOIN:
+ /** JOIN */
+ silc_fsm_next(fsm, silc_client_notify_join);
+ break;
+
+ case SILC_NOTIFY_TYPE_LEAVE:
+ /** LEAVE */
+ silc_fsm_next(fsm, silc_client_notify_leave);
+ break;
+
+ case SILC_NOTIFY_TYPE_SIGNOFF:
+ /** SIGNOFF */
+ silc_fsm_next(fsm, silc_client_notify_signoff);
+ break;
+
+ case SILC_NOTIFY_TYPE_TOPIC_SET:
+ /** TOPIC_SET */
+ silc_fsm_next(fsm, silc_client_notify_topic_set);
+ break;
+
+ case SILC_NOTIFY_TYPE_NICK_CHANGE:
+ /** NICK_CHANGE */
+ silc_fsm_next(fsm, silc_client_notify_nick_change);
+ break;
+
+ case SILC_NOTIFY_TYPE_CMODE_CHANGE:
+ /** CMODE_CHANGE */
+ silc_fsm_next(fsm, silc_client_notify_cmode_change);
+ break;
+
+ case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
+ /** CUMODE_CHANGE */
+ silc_fsm_next(fsm, silc_client_notify_cumode_change);
+ break;
+
+ case SILC_NOTIFY_TYPE_MOTD:
+ /** MOTD */
+ silc_fsm_next(fsm, silc_client_notify_motd);
+ break;
+
+ case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
+ /** CHANNEL_CHANGE */
+ silc_fsm_next(fsm, silc_client_notify_channel_change);
+ break;
+
+ case SILC_NOTIFY_TYPE_KICKED:
+ /** KICKED */
+ silc_fsm_next(fsm, silc_client_notify_kicked);
+ break;
+
+ case SILC_NOTIFY_TYPE_KILLED:
+ /** KILLED */
+ silc_fsm_next(fsm, silc_client_notify_killed);
+ break;
+
+ case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
+ /** SERVER_SIGNOFF */
+ silc_fsm_next(fsm, silc_client_notify_server_signoff);
+ break;
+
+ case SILC_NOTIFY_TYPE_ERROR:
+ /** ERROR */
+ silc_fsm_next(fsm, silc_client_notify_error);
+ break;
+
+ case SILC_NOTIFY_TYPE_WATCH:
+ /** WATCH */
+ silc_fsm_next(fsm, silc_client_notify_watch);
+ break;
+
+ default:
+ /** Unknown notify */
+ silc_notify_payload_free(payload);
+ silc_packet_free(packet);
+ silc_free(notify);
+ return SILC_FSM_FINISH;
+ break;
+ }
+
+ return SILC_FSM_CONTINUE;
+}
+
+/* Notify processed, finish the packet processing thread */
+
+SILC_FSM_STATE(silc_client_notify_processed)
+{
+ SilcClientNotify notify = state_context;
+ SilcPacket packet = notify->packet;
+ SilcNotifyPayload payload = notify->payload;
+
+ silc_notify_payload_free(payload);
+ silc_packet_free(packet);
+ silc_free(notify);
+ return SILC_FSM_FINISH;