#pragma module ADC_PROC "ADC_PROC-1-X" /* **++ ** FACILITY: ** ** MODULE DESCRIPTION: ** This module contains routines to process ADC specific command/request/actions ** ** ** AUTHORS: ** ** Ruslan R. Laishev ** ** CREATION DATE: 11-SEP-2009 ** ** DESIGN ISSUES: ** ** ** MODIFICATION HISTORY: ** ** 14-OCT-2009 RRL Added ADPING extension support. ** 27-NOV-2009 RRL Added checking minSS and minSF, ** fixed problem with forming AW0/AW1/AW2. ** ** {@tbs@}... **-- */ /* ** ** INCLUDE FILES ** */ #include #include #include #include #include #include #include #include #include #include #include #include "tiger.h" #define __NEW_STARLET 1 /* ** ** ADC DEFINITONS ** */ #include "macros.h" #include "adcdef.h" #include "adc_msg.h" extern ADC_ENT ctx_tbl[]; extern int ctx_tblsz; extern QUE work, tobekilled; extern ADC_VEC vec; int _adc_proc ( ADC_CTX * ctx, ADC_MSG * msg ) { int status; ASC * ni = &ctx->ctx$r_inf.inf$r_ni; if ( ctx && msg->msg$b_act == ADC$ACT_INF ) { ADC_INF *d = &ctx->ctx$r_inf, *s = &msg->msg$r_inf; ASC *as = NULL, *ad = NULL; /* We dont' allow to set Connection Type by remote Client if ( s->inf$l_inf & INF$M_CT ) d->inf$l_ct = s->inf$l_ct; */ if ( s->inf$l_inf & INF$M_DS ) d->inf$l_ds = s->inf$l_ds; if ( s->inf$l_inf & INF$M_AS ) d->inf$l_as = s->inf$l_as; if ( s->inf$l_inf & INF$M_AM ) d->inf$l_am = s->inf$l_am; if ( s->inf$l_inf & INF$M_AW ) d->inf$l_aw = s->inf$l_aw; if ( s->inf$l_inf & INF$M_SS ) d->inf$q_ss = s->inf$q_ss; if ( s->inf$l_inf & INF$M_SF ) d->inf$l_sf = s->inf$l_sf; if ( s->inf$l_inf & INF$M_SL ) d->inf$l_sl = s->inf$l_sl; if ( s->inf$l_inf & INF$M_HN ) d->inf$l_hn = s->inf$l_hn; if ( s->inf$l_inf & INF$M_HR ) d->inf$l_hr = s->inf$l_hr; if ( s->inf$l_inf & INF$M_HO ) d->inf$l_ho = s->inf$l_ho; if ( s->inf$l_inf & INF$M_US ) d->inf$l_us = s->inf$l_us; if ( s->inf$l_inf & INF$M_ID ) memcpy(&d->inf$r_id,&s->inf$r_id,sizeof(ASC)); if ( s->inf$l_inf & INF$M_PD ) memcpy(&d->inf$r_pd,&s->inf$r_pd,sizeof(ASC)); if ( s->inf$l_inf & INF$M_VE ) memcpy(&d->inf$r_ve,&s->inf$r_ve,sizeof(ASC)); if ( s->inf$l_inf & INF$M_EM ) memcpy(&d->inf$r_em,&s->inf$r_em,sizeof(ASC)); if ( s->inf$l_inf & INF$M_NI ) memcpy(&d->inf$r_ni,&s->inf$r_ni,sizeof(ASC)); if ( s->inf$l_inf & INF$M_DE ) memcpy(&d->inf$r_de,&s->inf$r_de,sizeof(ASC)); if ( s->inf$l_inf & INF$M_TO ) memcpy(&d->inf$r_to,&s->inf$r_to,sizeof(ASC)); if ( s->inf$l_inf & INF$M_SU ) memcpy(&d->inf$r_su,&s->inf$r_su,sizeof(ASC)); if ( s->inf$l_inf & INF$M_RF ) memcpy(&d->inf$r_rf,&s->inf$r_rf,sizeof(ASC)); /* ** Set IP and Port by using real connection context */ ctx->ctx$r_inf.inf$l_i4 = ctx->ctx$l_addr; ctx->ctx$r_inf.inf$l_u4 = ctx->ctx$w_port; } /* ** If Nickname/Username is local SYSUAF's users - send GPA */ if ( (ctx->ctx$l_state < CTX$M_IDENTIFY) && ni->asc_b_asc ) { ASC * un = &ctx->ctx$r_user; unsigned char pos; /* ** Is the NI in from "[Prefix]username" ? */ for ( pos = 0; (pos < ni->asc_b_asc) && (ni->asc_t_asc[pos] != ']'); pos++); _adc_trace(ctx,"NI[0:!UB]='!AC'",ni->asc_b_asc,ni); if ( (pos++) < ni->asc_b_asc ) { un->asc_b_asc = ni->asc_b_asc-pos; memcpy(un->asc_t_asc,&ni->asc_t_asc[pos],un->asc_b_asc); } else memcpy(un,ni,sizeof(ASC)); _adc_trace(ctx,"UN[0:!UB]='!AC'",un->asc_b_asc,un); if ( un->asc_b_asc ) { if ( 1 & chk_user(un->asc_t_asc,un->asc_b_asc) ) { status = _adc_gpa(ctx); ctx->ctx$l_state = CTX$M_VERIFY; } else ctx->ctx$l_state = 0; /* CTX$M_NORMAL; */ } else ctx->ctx$l_state = CTX$M_IDENTIFY; } /* ** We process messages addresed only to ALL and to HUB */ if ( (msg->msg$b_mt == ADC$MT_BROAD) || (msg->msg$b_mt == ADC$MT_FEAT) ) return _adc_broad(msg); else if ( msg->msg$b_mt == ADC$MT_DIRECT ) return _adc_direct(ctx); else if ( msg->msg$b_mt == ADC$MT_ECHO ) return _adc_echo(ctx); else if ( msg->msg$b_mt != ADC$MT_HUB ) return SS$_NORMAL; switch ( msg->msg$b_act ) { case ADC$ACT_SUP: status = _adc_sup(ctx); status = _adc_welcome(ctx); break; case ADC$ACT_INF: status = _adc_inf(ctx); break; case ADC$ACT_PAS: if ( ctx->ctx$l_state == CTX$M_VERIFY ) status = _adc_pas(ctx); break; default: break; } return SS$_NORMAL; } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Send to client at first connection time a list of SUPported ** features and options; ** ** FORMAL PARAMETERS: ** ** ctx: A session context ** ** RETURN VALUE: ** ** VMS condition code ** ** SIDE EFFECT: ** ** Set session context to NORMAL state ** **-- */ int _adc_sup ( ADC_CTX *ctx ) { unsigned status,sf = 0; unsigned __int64 ss = 0; $DESCRIPTOR(fao_dsc, "ISUP ADBASE0 ADBASE ADTIGR ADPING\nISID !XW"); struct dsc$descriptor buf_dsc; ADC_INF *inf = &ctx->ctx$r_inf; ADC_CTX *pctx; /* ** Send SUPported options */ INIT_SDESC(buf_dsc,sizeof(ctx->buf_b_buf),&ctx->buf_b_buf); if ( (1 & (status = sys$fao(&fao_dsc,&buf_dsc.dsc$w_length,&buf_dsc,ctx->ctx$v_sid))) ) if ( !(1 & (status = netio_write(ctx->ctx$l_chan,&buf_dsc))) ) if ( !(1 & (status = lib$insqti(ctx,&tobekilled,&RETRY_COUNT))) ) lib$signal(status); /* ** Send IINF message with PING extension, ** compute total SS and SF, number os connected clients */ for (int i = 0;i < ctx_tblsz; i++) { if ( !ctx_tbl[i].ctx$v_used ) continue; pctx = ctx_tbl[i].ctx$a_ptr; inf = &pctx->ctx$r_inf; ss += inf->inf$q_ss; sf += inf->inf$l_sf; } ctx->buf_w_len = _adc_format(ctx->buf_b_buf,sizeof(ctx->buf_b_buf), "IINF CT32 NI!AC DE!AC VE!AC HH!AC WS!AC OW!AC UC!UW SS!@UQ SF!UL MS!@UQ ML!UL", &vec.vec$r_ni,&vec.vec$r_de,&vec.vec$r_ve,&vec.vec$r_hh,&vec.vec$r_ws, &vec.vec$r_ow, vec.vec$w_uc,&ss,sf, &vec.vec$q_minss,vec.vec$l_minsl); INIT_SDESC(buf_dsc,sizeof(ctx->buf_b_buf),&ctx->buf_b_buf); if ( !(1 & (status = netio_write(ctx->ctx$l_chan,&buf_dsc))) ) if ( !(1 & (status = lib$insqti(ctx,&tobekilled,&RETRY_COUNT))) ) lib$signal(status); return status; } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Send to client (-s) INFormation. ** ** FORMAL PARAMETERS: ** ** ctx: A session context ** ** RETURN VALUE: ** ** VMS condition code ** ** **-- */ int _adc_inf ( ADC_CTX *ctx ) { int status,st; struct dsc$descriptor buf_dsc; QUE que = {0,0}; /* ** BINF (?) - broadcast the message to all connected clients */ INIT_SDESC(buf_dsc,ctx->buf_w_len,&ctx->buf_b_buf); /* ** Run over context/session table */ for (int i = 0;i < ctx_tblsz; i++) { if ( !ctx_tbl[i].ctx$v_used ) continue; ctx = ctx_tbl[i].ctx$a_ptr; if ( !(1 & (status = netio_write(ctx->ctx$l_chan,&buf_dsc))) ) if ( !(1 & (status = lib$insqti(ctx,&tobekilled,&RETRY_COUNT))) ) lib$signal(status); } return SS$_NORMAL; } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Broadcast has been received message to all connected clients. ** ** FORMAL PARAMETERS: ** ** ctx: A session context ** ** RETURN VALUE: ** ** VMS condition code ** ** **-- */ int _adc_broad ( ADC_MSG *msg ) { int status; struct dsc$descriptor buf_dsc; ADC_CTX *ctx = ctx_tbl[msg->msg$v_sid].ctx$a_ptr; ADC_INF *inf = &ctx->ctx$r_inf; if ( msg->msg$b_act == ADC$ACT_INF ) { /* ** If we need to bloadcast the INF message - form ** message string by using maintained data */ ctx->buf_w_len = _adc_format(ctx->buf_b_buf,sizeof(ctx->buf_b_buf), "BINF !XW CT!UL DS!UL AS!UL AM!UL SS!@UQ SF!UL SL!UL HN!UL HR!UL HO!UL U4!UW US!UL AW!UL", msg->msg$v_sid, inf->inf$l_ct,inf->inf$l_ds,inf->inf$l_as, inf->inf$l_am,&inf->inf$q_ss, inf->inf$l_sf,inf->inf$l_sl,inf->inf$l_hn, inf->inf$l_hr,inf->inf$l_ho, ctx->ctx$w_port, inf->inf$l_us,inf->inf$l_aw); ctx->buf_w_len -= inf->inf$l_aw?0:1; ctx->buf_w_len += _adc_format(&ctx->buf_b_buf[ctx->buf_w_len], sizeof(ctx->buf_b_buf)-ctx->buf_w_len, " ID!AC VE!AC EM!AC NI!AC DE!AC TO!AC SU!AC RF!AC I4!AC", &inf->inf$r_id,&inf->inf$r_ve, &inf->inf$r_em,&inf->inf$r_ni,&inf->inf$r_de, &inf->inf$r_to,&inf->inf$r_su,&inf->inf$r_rf, &ctx->ctx$r_addr); } /* ** In case other than INF message check that client ** accept conditions, minimum: Shared Size, Shared Files, Slots */ else if ( !ctx->ctx$v_normal ) { if ( vec.vec$l_minsf && (vec.vec$l_minsf > inf->inf$l_sf) ) return _adc_sta(ctx,ADCP_MINSF,inf->inf$l_sf,vec.vec$l_minsf); else if ( vec.vec$q_minss && (vec.vec$q_minss > inf->inf$q_ss) ) return _adc_sta(ctx,ADCP_MINSS,inf->inf$q_ss,vec.vec$q_minss); else if ( vec.vec$l_minsl && (vec.vec$l_minsl > inf->inf$l_sl) ) return _adc_sta(ctx,ADCP_MINSL,inf->inf$l_sl,vec.vec$l_minsl); } /* ** BINF (?) - broadcast the message to all connected clients */ INIT_SDESC(buf_dsc,ctx->buf_w_len,&ctx->buf_b_buf); /* ** Run over context/session table */ for (int i = 0;i < ctx_tblsz; i++) { if ( !ctx_tbl[i].ctx$v_used ) continue; ctx = ctx_tbl[i].ctx$a_ptr; if ( !(1 & (status = netio_write(ctx->ctx$l_chan,&buf_dsc))) ) if ( !(1 & (status = lib$insqti(ctx,&tobekilled,&RETRY_COUNT))) ) lib$signal(status); } return SS$_NORMAL; } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Retrive "welcome" message from the ADC_WELCOME_MSG.TXT, ** send it to new connected client. ** ** FORMAL PARAMETERS: ** ** ctx: A session context ** ** RETURN VALUE: ** ** VMS condition code ** ** **-- */ int _adc_welcome ( ADC_CTX *ctx ) { int status, bufl = 0,inpleft = 0,outleft = 0; static struct dsc$descriptor buf_dsc; struct dsc$descriptor inp_dsc; struct FAB fab; struct RAB rab; const char fname [] = "ADC_WELCOME_MSG", fext [] = ".TXT"; static int welcome = 0; char buf[1024],*cp,*cp2; $DESCRIPTOR(cr_dsc, "\n"); $DESCRIPTOR(imsg_dsc, "ISTA 000 Powered\\sby\\sStarLet\\sADC\\sHub\\sfor\\sOpenVMS\nIMSG "); $DESCRIPTOR(binf_dsc, "BINF !XW "); ADC_CTX *pctx; ADC_INF *inf; static iconv_t cd; /* ** Load and encode WELCOME message from file */ if ( !welcome ) { char convbuf [ 1024 ]; fab = cc$rms_fab; fab.fab$b_fac = FAB$M_GET; fab.fab$b_shr = FAB$M_SHRGET; fab.fab$l_fna = &fname; fab.fab$b_fns = sizeof(fname)-1; fab.fab$l_dna = &fext; fab.fab$b_dns = sizeof(fext)-1; if ( !(1 & (status = sys$open (&fab))) ) lib$signal(status); rab = cc$rms_rab; rab.rab$l_fab = &fab; if ( !(1 & (status = sys$connect (&rab))) ) lib$signal(status); rab.rab$w_usz = sizeof(buf); rab.rab$l_ubf = buf; if ( !(cd = iconv_open("UTF-8","ISO8859-5")) ) perror("iconv_open"); INIT_DDESC(buf_dsc); for(welcome = 0; 1 & (status = sys$get(&rab)); welcome++ ) { /* ** ISO8859-5 to UTF-8 */ cp = &buf; cp2 = &convbuf; inpleft = rab.rab$w_rsz; outleft = sizeof(convbuf); if ( 0 > (status = iconv (cd,&cp,&inpleft,&cp2,&outleft)) ) perror("iconv"); INIT_SDESC(inp_dsc,sizeof(convbuf)-outleft,convbuf); if ( !(1 & (status = str$concat(&buf_dsc,&buf_dsc,&cr_dsc,&inp_dsc))) ) lib$signal(status); } if ( !(1 & (status = __adc_enc(&buf_dsc,&buf_dsc))) ) lib$signal(status); if ( !(1 & (status = str$concat(&buf_dsc,&imsg_dsc,&buf_dsc))) ) lib$signal(status); status = sys$close(&fab); } /* ** Send Welcome message */ if ( !(1 & (status = netio_write(ctx->ctx$l_chan,&buf_dsc))) ) { if ( !(1 & (status = lib$insqti(ctx,&tobekilled,&RETRY_COUNT))) ) lib$signal(status); return status; } /* ** Send to new connected client information about of all ** has been connectecetd clients */ for (int i = 0;i < ctx_tblsz; i++) { pctx = ctx_tbl[i].ctx$a_ptr; if ( (ctx_tbl[i].ctx$l_flag != CTX$M_USED) || (pctx->ctx$v_sid == ctx->ctx$v_sid) ) continue; inf = &pctx->ctx$r_inf; ctx->buf_w_len = _adc_format(ctx->buf_b_buf,sizeof(ctx->buf_b_buf), "BINF !XW CT!UL DS!UL AS!UL AM!UL SS!@UQ SF!UL SL!UL HN!UL HR!UL HO!UL U4!UW US!UL AW!UL", pctx->ctx$v_sid, inf->inf$l_ct,inf->inf$l_ds,inf->inf$l_as, inf->inf$l_am,&inf->inf$q_ss, inf->inf$l_sf,inf->inf$l_sl,inf->inf$l_hn, inf->inf$l_hr,inf->inf$l_ho, pctx->ctx$w_port, inf->inf$l_us,inf->inf$l_aw); ctx->buf_w_len -= inf->inf$l_aw?0:1; ctx->buf_w_len += _adc_format(&ctx->buf_b_buf[ctx->buf_w_len], sizeof(ctx->buf_b_buf)-ctx->buf_w_len, " ID!AC VE!AC EM!AC NI!AC DE!AC TO!AC SU!AC RF!AC I4!AC", &inf->inf$r_id,&inf->inf$r_ve, &inf->inf$r_em,&inf->inf$r_ni,&inf->inf$r_de, &inf->inf$r_to,&inf->inf$r_su,&inf->inf$r_rf, &pctx->ctx$r_addr); /* ** Send Welcome message */ INIT_SDESC(inp_dsc,ctx->buf_w_len,&ctx->buf_b_buf); if ( !(1 & (status = netio_write(ctx->ctx$l_chan,&inp_dsc))) ) { if ( !(1 & (status = lib$insqti(ctx,&tobekilled,&RETRY_COUNT))) ) lib$signal(status); return status; } } return status; } int _adc_gpa ( ADC_CTX *ctx ) { int status, bufl = 0; char buf[64]; struct dsc$descriptor dsc; /* ** Ask for password */ bufl = _adc_base32enc(&ctx->ctx$b_salt,sizeof(ctx->ctx$b_salt),&buf); _adc_trace(ctx,"Salt[0:!UW]='!AD'",bufl,bufl,buf); ctx->buf_w_len = _adc_format(ctx->buf_b_buf,sizeof(ctx->buf_b_buf), "IGPA !AD",bufl,buf); INIT_SDESC(dsc,ctx->buf_w_len,&ctx->buf_b_buf); if ( !(1 & (status = netio_write(ctx->ctx$l_chan,&dsc))) ) if ( !(1 & (status = lib$insqti(ctx,&tobekilled,&RETRY_COUNT))) ) lib$signal(status); return status; } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Process DIRECT messages. ** ** FORMAL PARAMETERS: ** ** ctx: A session context ** ** RETURN VALUE: ** ** VMS condition code ** ** SIDE EFFECT: ** ** Set session context to NORMAL state ** **-- */ int _adc_direct ( ADC_CTX *ctx ) { int status, to = 0; char buf[32]; struct dsc$descriptor buf_dsc; ADC_INF *inf = &ctx->ctx$r_inf; /* ** In case other than INF message check that client ** accept conditions, minimum: Shared Size, Shared Files, Slots */ if ( !ctx->ctx$v_normal ) { if ( vec.vec$l_minsf && (vec.vec$l_minsf > inf->inf$l_sf) ) return _adc_sta(ctx,ADCP_MINSF,inf->inf$l_sf,vec.vec$l_minsf); else if ( vec.vec$q_minss && (vec.vec$q_minss > inf->inf$q_ss) ) return _adc_sta(ctx,ADCP_MINSS,inf->inf$q_ss,vec.vec$q_minss); else if ( vec.vec$l_minsl && (vec.vec$l_minsl > inf->inf$l_sl) ) return _adc_sta(ctx,ADCP_MINSL,inf->inf$l_sl,vec.vec$l_minsl); } INIT_SDESC(buf_dsc,ctx->buf_w_len,&ctx->buf_b_buf); if ( (3 != sscanf(ctx->buf_b_buf,"%s %x %x",&buf,&to,&to)) ||(to > ctx_tblsz) ) return _adc_log(ADC_MALFORMED,ctx->buf_w_len,ctx->buf_b_buf); ctx = ctx_tbl[to].ctx$a_ptr; /* ** Send Welcome message */ if ( !(1 & (status = netio_write(ctx->ctx$l_chan,&buf_dsc))) ) { /* ** Error ? Put the "bad session" context into ** queue for contexts to be deleted */ if ( !(1 & (status = lib$insqti(ctx,&tobekilled,&RETRY_COUNT))) ) lib$signal(status); return status; } return SS$_NORMAL; } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Process ECHO messages. ** ** FORMAL PARAMETERS: ** ** ctx: A session context ** ** RETURN VALUE: ** ** VMS condition code ** ** SIDE EFFECT: ** ** Set session context to NORMAL state ** **-- */ int _adc_echo ( ADC_CTX *ctx ) { int status, from = 0, to = 0; char buf[32]; struct dsc$descriptor buf_dsc; if ( (3 != sscanf(ctx->buf_b_buf,"%s %x %x",&buf,&from,&to)) || (to > ctx_tblsz) || (from != ctx->ctx$v_sid) ) return _adc_log(ADC_MALFORMED,ctx->buf_w_len,ctx->buf_b_buf); INIT_SDESC(buf_dsc,ctx->buf_w_len,&ctx->buf_b_buf); /* ** Send Welcome message */ if ( !(1 & (status = netio_write(ctx->ctx$l_chan,&buf_dsc))) ) { if ( !(1 & (status = lib$insqti(ctx,&tobekilled,&RETRY_COUNT))) ) lib$signal(status); return status; } ctx = ctx_tbl[to].ctx$a_ptr; /* ** Send Welcome message */ if ( !(1 & (status = netio_write(ctx->ctx$l_chan,&buf_dsc))) ) { if ( !(1 & (status = lib$insqti(ctx,&tobekilled,&RETRY_COUNT))) ) lib$signal(status); return status; } return SS$_NORMAL; } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Process ECHO messages. ** ** FORMAL PARAMETERS: ** ** ctx: A session context ** ** RETURN VALUE: ** ** VMS condition code ** ** SIDE EFFECT: ** ** Set session context to NORMAL state ** **-- */ int _adc_pas ( ADC_CTX *ctx ) { int status,bufl = 0,passlen = 0; char thash[TIGER_DIGEST_LENGTH],buf[128], pass [32]; TIGER_CTX td; ASC * cid = &ctx->ctx$r_inf.inf$r_id, * un = &ctx->ctx$r_user; ADC_INF *inf = &ctx->ctx$r_inf; /* ** Get user's UIC */ if ( !(1 & (status = get_uic(un->asc_t_asc,un->asc_b_asc, pass,sizeof(pass),&passlen))) ) return _adc_log(status); /* ** */ tiger_init(&td); tiger_update(&td,pass,passlen); tiger_update(&td,ctx->ctx$b_salt,sizeof(ctx->ctx$b_salt)); tiger_final(&td,&thash); bufl =_adc_base32enc(&thash,sizeof(thash),buf); _adc_trace(ctx,"Hash[0:!UW]='!AD'",bufl,bufl,buf); if ( !memcmp(buf,&ctx->buf_b_buf[5],bufl) ) { /* ** User now is - Registered User */ inf->inf$l_ct = INF$M_USER; return SS$_NORMAL; } /* ** Old variant of the hash computing */ tiger_init(&td); bufl =_adc_base32dec(cid->asc_t_asc,cid->asc_b_asc,buf); tiger_update(&td,buf,bufl); tiger_update(&td,pass,passlen); tiger_update(&td,ctx->ctx$b_salt,sizeof(ctx->ctx$b_salt)); tiger_final(&td,&thash); bufl =_adc_base32enc(&thash,sizeof(thash),&buf); _adc_trace(ctx,"Hash[0:!UW]='!AD'",bufl,bufl,buf); if ( !memcmp(buf,&ctx->buf_b_buf[5],bufl) ) { /* ** User now is - Registered User */ inf->inf$l_ct = INF$M_USER; return SS$_NORMAL; } return SS$_INVLOGIN; } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Process QUIT messages. ** ** FORMAL PARAMETERS: ** ** ctx: A session context ** ** RETURN VALUE: ** ** VMS condition code ** ** SIDE EFFECT: ** ** Set session context to NORMAL state ** **-- */ int _adc_qui ( ADC_CTX *ctx ) { int status,bufl = 0; unsigned char buf [64]; struct dsc$descriptor inp_dsc; ADC_CTX *pctx; bufl = _adc_format(&buf,sizeof(buf),"IQUI !XW",ctx->ctx$v_sid); INIT_SDESC(inp_dsc,bufl,buf); for (int i = 0;i < ctx_tblsz; i++) { pctx = ctx_tbl[i].ctx$a_ptr; if ( (ctx_tbl[i].ctx$l_flag != CTX$M_USED) || (pctx->ctx$v_sid == ctx->ctx$v_sid) ) continue; if ( !(1 & (status = netio_write(pctx->ctx$l_chan,&inp_dsc))) ) if ( !(1 & (status = lib$insqti(pctx,&tobekilled,&RETRY_COUNT))) ) lib$signal(status); } return SS$_NORMAL; } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Send to client at first connection time a list of SUPported ** features and options; ** ** FORMAL PARAMETERS: ** ** ctx: A session context ** ** RETURN VALUE: ** ** VMS condition code ** ** SIDE EFFECT: ** ** Set session context to NORMAL state ** **-- */ int _adc_sta ( ADC_CTX *ctx, int msgid, ... ) { int status,argc,argl[32],idx,flag=15; struct dsc$descriptor msg_dsc,fao_dsc; va_list ap; char fao_buf[128],outadr[4]; /* ** Get a message text with given msgid */ INIT_SDESC(fao_dsc,sizeof(fao_buf),fao_buf); if ( !(1 & (status = sys$getmsg (msgid,&fao_dsc.dsc$w_length,&fao_dsc,flag,&outadr))) ) lib$signal(status); va_start(ap,msgid); for (idx = 0,va_count(argc); idx < argc; idx++) argl[idx] = va_arg(ap,unsigned); va_end((void *) msgid); /* ** Format message to buffer */ INIT_SDESC(msg_dsc,sizeof(ctx->buf_b_buf),&ctx->buf_b_buf); if ( !(1 & (status = sys$faol(&fao_dsc,&msg_dsc.dsc$w_length,&msg_dsc,argl))) ) lib$signal(status); /* ** Send formated ISTAtus message to the client */ if ( !(1 & (status = netio_write(ctx->ctx$l_chan,&msg_dsc))) ) if ( !(1 & (status = lib$insqti(ctx,&tobekilled,&RETRY_COUNT))) ) lib$signal(status); _adc_trace(ctx,"!AS",&msg_dsc); return status; }