2 'sys$Library:PASCAL$LIB_ROUTINES')]
3 PROGRAM ACM_SHOPFLOOR(OUTPUT);
4 { }
5 { AUTHENTICATE - major subroutine of this module }
6 { }
7 { This function is called with a USER_INDEX, indicating which }
8 { of 10 buttons on the shop floor kiosk was pushed, and thus }
9 { which of ten employees is to be authenticated. }
10 { }
11 TYPE PRINCIPAL_INDEX_TYPE = (
12 PRINCIPAL_1,
13 PRINCIPAL_2,
14 PRINCIPAL_3,
15 PRINCIPAL_4,
16 PRINCIPAL_5,
17 PRINCIPAL_6,
18 PRINCIPAL_7,
19 PRINCIPAL_8,
20 PRINCIPAL_9,
21 PRINCIPAL_10 );
22 { }
23 { This subroutine translates each of the 10 possible index }
24 { values into one of ten generic principal names. To avoid }
25 { changes to this client program, those principal names are }
26 { mapped into the principal names actually corresponding to }
27 { individual names within the ACME Server, so that a single }
28 { data file can be modified by a designated administrator }
29 { without changing the client software. }
30 { }
31 { }
32 { After the Principal Name has been determined, the user must }
33 { be authenticated. At some kiosks there is a fingerprint }
34 { reader that will be used for authentication, while at the }
35 { spray painting station a keyboard is always used because }
36 { employees are wearing rubber gloves. For some sensitive }
37 { combinations of Principal Name and kiosk, a fingerprint }
38 { and passwords might both be required. These variations, }
39 { however, are determined by ACMEs within the ACME Server, }
40 { and this client code merely authenticates using whatever }
41 { method might be specified in the Context Area returned by }
42 { successive SYS$ACM calls. }
43 { }
44 CONST
45 FINGERPRINT_READIT = 32770; { from the Fingerprint ACME }
46 { }
47 { After authentication it is also possible that password }
48 { expirations may need to be handled, in which case even in }
49 { situations where a fingerprint would normally be sufficient,}
50 { the user will actually have to engage in typing. Whether }
51 { users who normally authenticate with a fingerprint even }
52 { have a password is an administrative issue enforced by }
53 { configuration of the ACMEs. As in the authentication step, }
54 { this client software just implements whatever mechanism is }
55 { specified in the Context Area returned by successive }
56 { SYS$ACM calls. }
57 { }
58 FUNCTION AUTHENTICATE ( PRINCIPAL_INDEX : PRINCIPAL_INDEX_TYPE ):BOOLEAN;
59 TYPE
60 ACMECB_PTR = ^ACMECB$TYPE;
61 CHANNEL_TYPE = [WORD] 0..65535;
62 VAR
63 FINGERPRINT_READER_CHANNEL : CHANNEL_TYPE VALUE 0;
64 TERMINAL_CHANNEL : CHANNEL_TYPE VALUE 0;
65 MY_LOGON_TYPE : INTEGER VALUE ACME$K_LOCAL;
66 MY_DIALOGUE_SUPPORT : INTEGER
67 VALUE ACMEDLOGFLG$M_INPUT + ACMEDLOGFLG$M_NOECHO;
68 { }
69 { We rely on an initial query to determine the ACME ID }
70 { of the Fingerprint ACME in the current running system. }
71 { We use that ACME ID to compare against ACMECB$L_ACME_ID }
72 { in the ACME Communications Buffer to determine whether }
73 { an ACME-specific input item set is one created by the }
74 { Fingerprint ACME, because ACME-specific item codes must }
75 { qualified by the originating ACME. }
76 { }
77 { Field ACMECB$L_ACME_ID.ACMEID$V_ACME_NUM will be the }
78 { actual basis of comparison, because it is sufficient to }
79 { identify a particular ACME and the other fields within }
80 { an ACME ID might change between when our query call }
81 { completes and when we make our authenticate call. }
82 { }
83 { We make our query against the reserved ID value of 0, }
84 { to gather information about the ACME Agents. This query }
85 { is actually handled by the SYS$ACMW system service. }
86 { }
87 { Data elements for the query for ACME ID }
88 { }
89 { Addresses of these elements will be set into item list }
90 { ACM_QUERY_ITMLST by procedural code below. }
91 { }
92 SYS$ACM_ACME_ID : INTEGER VALUE 0;
93 ACME_QUERY_ACME_NAME : INTEGER VALUE ACME$K_QUERY_ACME_NAME;
94 FINGERPRINT_ACME_NAME : STRING(16) VALUE 'FINGERPRINT_ACME';
95 ACME_TARGET_DOI_ID : INTEGER VALUE ACME$K_QUERY_ACME_ID;
96 FINGERPRINT_ACME_ID : ACMEID$TYPE;
97 { }
98 { Item list for the Query }
99 { }
100 ACM_QUERY_ITMLST : ARRAY[0..5] OF ILE3$TYPE
101 VALUE [ 0:[ILE3$W_LENGTH:4;
102 ILE3$W_CODE:ACME$_TARGET_DOI_ID;
103 ILE3$PS_BUFADDR:0;
104 ILE3$PS_RETLEN_ADDR:NIL];
105 1:[ILE3$W_LENGTH:4;
106 ILE3$W_CODE:ACME$_QUERY_KEY_TYPE;
107 ILE3$PS_BUFADDR:0;
108 ILE3$PS_RETLEN_ADDR:NIL];
109 2:[ILE3$W_LENGTH:16;
110 ILE3$W_CODE:ACME$_QUERY_KEY_VALUE;
111 ILE3$PS_BUFADDR:0;
112 ILE3$PS_RETLEN_ADDR:NIL];
113 3:[ILE3$W_LENGTH:4;
114 ILE3$W_CODE:ACME$_QUERY_TYPE;
115 ILE3$PS_BUFADDR:0;
116 ILE3$PS_RETLEN_ADDR:NIL];
117 4:[ILE3$W_LENGTH:4;
118 ILE3$W_CODE:ACME$_QUERY_DATA;
119 ILE3$PS_BUFADDR:0;
120 ILE3$PS_RETLEN_ADDR:NIL];
121 5:[ILE3$W_LENGTH:0;
122 ILE3$W_CODE:0;
123 ILE3$PS_BUFADDR:0;
124 ILE3$PS_RETLEN_ADDR:NIL]];
125 { }
126 { Item list for initial Authentication call }
127 { }
128 MY_ACM_ITMLST_A : ARRAY[0..2] OF ILE3$TYPE
129 VALUE [ 0:[ILE3$W_LENGTH:4;
130 ILE3$W_CODE:ACME$_LOGON_TYPE;
131 ILE3$PS_BUFADDR:0;
132 ILE3$PS_RETLEN_ADDR:NIL];
133 1:[ILE3$W_LENGTH:4;
134 ILE3$W_CODE:ACME$_DIALOGUE_SUPPORT;
135 ILE3$PS_BUFADDR:0;
136 ILE3$PS_RETLEN_ADDR:NIL];
137 2:[ILE3$W_LENGTH:0;
138 ILE3$W_CODE:0;
139 ILE3$PS_BUFADDR:0;
140 ILE3$PS_RETLEN_ADDR:NIL]];
141 { }
142 { Variables used both inside and outside Function RESPOND }
143 { }
144 MY_ACMESB : ACMESB$TYPE;
145 MY_CONTXT : ACMECB_PTR;
146 MY_STATUS : UNSIGNED;
147 TRASH_STATUS : UNSIGNED;
148 { }
149 { The ITEMSET array we will read }
150 { }
151 TYPE
152 { }
153 { A string longer than we will ever see, defined to }
154 { avoid exceeding Pascal's 2**16-1 limit on string }
155 { length. }
156 { }
157 CHAR_ARRAY_TYPE = PACKED ARRAY [1..65535]
158 OF CHAR;
159 CHAR_ARRAY_TYPE_POINTER = ^CHAR_ARRAY_TYPE;
160 { }
161 { An array longer than we will ever see, defined to }
162 { avoid: }
163 { }
164 { "%PASCAL-E-SIZGTRMAX, Size exceeds MAXINT bits". }
165 { }
166 ITEMSET_ARRAY_TYPE =
167 PACKED ARRAY [1..MAXINT DIV (ACMEIS$K_LENGTH*8)]
168 OF ACMEITMSET$TYPE;
169 ITEMSET_ARRAY_TYPE_POINTER = ^ITEMSET_ARRAY_TYPE;
170 VAR
171 ITEMSET_ARRAY : ITEMSET_ARRAY_TYPE_POINTER;
172 { }
173 { A special declaration is required in order to }
174 { Synchronize on an ACM Status Block }
175 { }
176 [ASYNCHRONOUS,EXTERNAL(SYS$SYNCH)] FUNCTION $SYNCH_ACMESB (
177 %IMMED EFN : UNSIGNED := %IMMED 0;
178 VAR IOSB : [VOLATILE] ACMESB$TYPE := %IMMED 0)
179 : INTEGER; EXTERNAL;
180 { }
181 { Function to fill in responses to input itemsets }
182 { }
183 { Input itemsets will require buffer space, and }
184 { although each input itemset will use no more }
185 { than 65535 bytes, the number of input itemsets }
186 { provided in a single dialogue step is not }
187 { bounded. }
188 { }
189 { Therefore we invoke this function recursively }
190 { each time we encounter an input itemset, }
191 { making use of a conformant parameter to }
192 { allocate the appropriate length buffer. When }
193 { all itemsets have been processed, we make our }
194 { continuation call to $ACM from the deepest }
195 { level of recursion (when all buffers are still }
196 { intact), and then return from function RESPOND }
197 { entirely to wait for completion of the call. }
198 { }
199 { This recursive approach using stack-based }
200 { buffers is fine for operation on the expandable }
201 { main VMS user-mode stack, but an application }
202 { operating on non-expandable stacks, such as }
203 { non-initial stack from VAX Ada or DECthreads, }
204 { should obviously use iteration and heap-based }
205 { explicit allocation instead. }
206 { }
207 FUNCTION RESPOND ( ITEMSET_COUNT : INTEGER ):INTEGER;
208 { }
209 { The Item List we will write for use on the }
210 { next call to SYS$ACM will never have more }
211 { entries than the Itemset List we received }
212 { in the ACM Communications Buffer from the }
213 { previous call to SYS$ACM, so we choose that }
214 { maximum size for our item list. }
215 { }
216 TYPE
217 ITEM_LIST_TEMPLATE ( UPPER_BOUND : INTEGER )
218 = ARRAY [1..UPPER_BOUND] OF ILE3$TYPE;
219 VAR
220 ITEM_LIST : ITEM_LIST_TEMPLATE ( ITEMSET_COUNT + 1 );
221 EACH_ITEM : INTEGER VALUE 1;
222 { }
223 { Each invocation of RECURSE_OVER_ITEMS will }
224 { allocate an automatic (stack-based) buffer. }
225 { }
226 TYPE
227 INPUT_BUFFER_TEMPLATE ( MAX_SIZE : INTEGER )
228 = PACKED ARRAY [1..MAX_SIZE] OF CHAR;
229 { }
230 { Variables for parsing the Itemset List }
231 { }
232 VAR
233 CHAR_ARRAY_LENGTH_1 : INTEGER;
234 CHAR_ARRAY_POINTER_1 : CHAR_ARRAY_TYPE_POINTER;
235 CHAR_ARRAY_LENGTH_2 : INTEGER;
236 CHAR_ARRAY_POINTER_2 : CHAR_ARRAY_TYPE_POINTER;
237 EACH_ITEMSET : INTEGER VALUE 1;
238 INPUT_IOSB, CONFIRM_IOSB : IOSB$TYPE;
239 { }
240 { RECURSE_OVER_ITEMS }
241 { }
242 { This function gets called: }
243 { }
244 { 1. Once with a parameter of zero at the }
245 { start of processing an Itemset List. }
246 { }
247 { 2. Recursively as each input itemset is }
248 { encountered in the Itemset List. }
249 { }
250 { Multiple output itemsets are processed at a }
251 { single recursion level until the end of the }
252 { Itemset List or until an input itemset }
253 { is found. }
254 FUNCTION RECURSE_OVER_ITEMS ( MAX_SIZE : INTEGER ):INTEGER;
255 { }
256 { The buffer we will use for this input item }
257 { }
258 { The INPUT_BUFFER lifetime needs only be for }
259 { the lifetime of RECURSE_OVER_ITEMS because it }
260 { is filled by SYS$QIOW at this recursion }
261 { level and provided as input to SYS$ACM at }
262 { the innermost recursion level. }
263 { }
264 VAR
265 { }
266 { We use MAX_SIZE+1 to avoid the error: }
267 { }
268 { %PAS-F-LOWGTRHIGH, low-bound exceeds high-bound }
269 { }
270 { when MAX_SIZE is 0. }
271 { }
272 INPUT_BUFFER : INPUT_BUFFER_TEMPLATE ( MAX_SIZE+1 );
273 CONFIRM_BUFFER : INPUT_BUFFER_TEMPLATE ( MAX_SIZE+1 );
274 QIO_FUNC : INTEGER;
275 { }
276 PROCEDURE WRITE_ITEM_PLAIN;
277 BEGIN { WRITE_ITEM_PLAIN }
278 IF CHAR_ARRAY_POINTER_1 <> NIL
279 THEN
280 IF CHAR_ARRAY_LENGTH_1 = 0
281 THEN
282 WRITELN
283 ELSE
284 WRITELN (
285 CHAR_ARRAY_POINTER_1^[1..
286 CHAR_ARRAY_LENGTH_1] );
287 IF CHAR_ARRAY_POINTER_2 <> NIL
288 THEN
289 IF CHAR_ARRAY_LENGTH_2 = 0
290 THEN
291 WRITELN
292 ELSE
293 WRITELN (
294 CHAR_ARRAY_POINTER_2^[1..
295 CHAR_ARRAY_LENGTH_2] );
296 END; { WRITE_ITEM_PLAIN }
297 { }
298 PROCEDURE SET_BUFFER (
299 PRINCIPAL_NAME : STRING );
300 BEGIN { PROCEDURE SET_BUFFER }
301 INPUT_IOSB.IOSB$W_BCNT :=
302 MIN ( SIZE ( PRINCIPAL_NAME ),
303 SIZE ( INPUT_BUFFER ) );
304 { }
305 { The following line will produce a }
306 { Pascal run-time error if SYS$ACM does }
307 { not specify input lengths of at least }
308 { 12 characters. }
309 { }
310 READV ( PRINCIPAL_NAME, INPUT_BUFFER );
311 { }
312 END; { PROCEDURE SET_BUFFER }
313 { }
314 BEGIN { FUNCTION RECURSE_OVER_ITEMS }
315 { }
316 { Process any initial Input Itemset }
317 { }
318 IF MAX_SIZE <> 0
319 THEN
320 BEGIN { process Input Itemset }
321 { }
322 { First we consider non-text ACME-specific }
323 { item codes, and the only one of those we }
324 { are prepared to handle is the Fingerprint }
325 { ACME code FINGERPRINT_READIT. }
326 { }
327 IF ITEMSET_ARRAY^[EACH_ITEMSET]
328 .ACMEIS$W_ITEM_CODE.ACMEIC$V_ACME_SPECIFIC
329 AND NOT ITEMSET_ARRAY^[EACH_ITEMSET]
330 .ACMEIS$W_ITEM_CODE.ACMEIC$V_UCS
331 THEN
332 BEGIN { ACME-specific non-text input }
333 { }
334 { Comparing MY_CONTXT^.ACMECB$L_ACME_ID }
335 { .ACMEID$V_ACME_NUM field against the }
336 { (previously queried) IDs of ACMEs from }
337 { which this client expects ACME-specific}
338 { input itemsets and also comparing
339 { }
340 { ITEMSET_ARRAY^[EACH_ITEMSET] }
341 { .ACMEIS$W_ITEM_CODE.ACMEIC$W_ITEM_CODE}
342 { against the 16-bit values of expected }
343 { ACME-specific item codes, we get the }
344 { information to dispatch to handle each }
345 { of the ACME-specific message types that}
346 { this client program knows about. }
347 { }
348 { In our case, it is only the Fingerprint}
349 { ACME and only code FINGERPRINT_READIT. }
350 { }
351 ASSERT((MY_CONTXT^.ACMECB$L_ACME_ID.ACMEID$V_ACME_NUM
352 = FINGERPRINT_ACME_ID.ACMEID$V_ACME_NUM)
353 AND (ITEMSET_ARRAY^[EACH_ITEMSET]
354 .ACMEIS$W_ITEM_CODE
355 .ACMEIC$W_ITEM_CODE
356 = FINGERPRINT_READIT ),
357 'unknown ACME-specific item code');
358 { }
359 { Exchange Fingerprint Data }
360 { }
361 { This client contains little knowledge }
362 { regarding the workings of the }
363 { Fingerprint Reader. It knows to call }
364 { SYS$QIOW using the function code }
365 { IO$_READPROMPT providing the output }
366 { "prompt" data and accepting whatever }
367 { the device provides. Buffer sizes }
368 { (within the 65535 limit) and the number}
369 { of exchanges to read a fingerprint }
370 { are governed by the Fingerprint ACME, }
371 { which has knowledge of the device }
372 { characteristics. }
373 { }
374 { Perhaps the channel is open from a }
375 { previous dialogue or recursion step. }
376 { }
377 IF FINGERPRINT_READER_CHANNEL = 0
378 THEN
379 BEGIN { a channel must be assigned }
380 MY_STATUS :=
381 $ASSIGN (
382 DEVNAM := 'FPA0:',
383 CHAN := FINGERPRINT_READER_CHANNEL );
384 { }
385 { If there is no Fingerprint Reader }
386 { on this machine, the Fingerprint }
387 { ACME should have figured that out }
388 { and not requested Fingerprint }
389 { Reader data. }
390 { }
391 IF NOT ODD(MY_STATUS)
392 then
393 RETURN MY_STATUS;
394 END; { A channel must be assigned.}
395 { }
396 { Exchange Fingerprint data }
397 { }
398 MY_STATUS :=
399 $QIOW (
400 EFN := EFN$C_ENF,
401 CHAN := FINGERPRINT_READER_CHANNEL,
402 FUNC := IO$_READPROMPT,
403 IOSB := INPUT_IOSB,
404 P1 := INPUT_BUFFER,
405 P2 := SIZE(INPUT_BUFFER),
406 P5 := IADDRESS(CHAR_ARRAY_POINTER_1^),
407 P6 := CHAR_ARRAY_LENGTH_1 );
408 IF ODD(MY_STATUS)
409 THEN
410 MY_STATUS := INPUT_IOSB.IOSB$W_STATUS;
411 IF NOT ODD(MY_STATUS)
412 THEN
413 RETURN MY_STATUS;
414 { }
415 END { ACME-specific non-text input }
416 ELSE
417 BEGIN { general or text input itemset }
418 { }
419 { Pascal does not give us the ability }
420 { that more strongly typed languages do }
421 { to force a compile-time failure in the }
422 { case where new message types have been }
423 { added to a subsequent release of VMS, }
424 { so we make these run-time checks. }
425 { }
426 ASSERT(ACMEMC$K_MIN_GEN_MSG
427 = ACMEMC$K_GENERAL,
428 'ACMEMC$K_MIN_GEN_MSG has shifted');
429 ASSERT(ACMEMC$K_MAX_GEN_MSG
430 = ACMEMC$K_DIALOGUE_ALERT,
431 'ACMEMC$K_MAX_GEN_MSG has shifted');
432 ASSERT(ACMEMC$K_MIN_LOGON_MSG
433 = ACMEMC$K_SYSTEM_IDENTIFICATION,
434 'ACMEMC$K_MIN_LOGON_MSG has shifted');
435 ASSERT(ACMEMC$K_MAX_LOGON_MSG
436 = ACMEMC$K_MAIL_NOTICES,
437 'ACMEMC$K_MAX_LOGON_MSG has shifted');
438 { }
439 { The only general item codes we know of }
440 { for input itemsets are those that are }
441 { "well known items", and those all }
442 { carry text. To be flexible for any }
443 { possible future additions, however, }
444 { we choose to handle any text input }
445 { item code, and we can detect those }
446 { by looking at bit ACMEIC$V_UCS in }
447 { the item code. That bit is simply a }
448 { predefined characteristic of the item }
449 { code and is quite independent of }
450 { whether or not a particular caller }
451 { of SYS$ACM might set the ACME$V_UCS2_4 }
452 { function modifier to indicate strings }
453 { are provided in UCS format. }
454 { }
455 IF ITEMSET_ARRAY^[EACH_ITEMSET]
456 .ACMEIS$W_ITEM_CODE.ACMEIC$V_UCS
457 THEN
458 IF ITEMSET_ARRAY^[EACH_ITEMSET]
459 .ACMEIS$W_ITEM_CODE.ACMEIC$W_ITEM_CODE
460 = ACME$_PRINCIPAL_NAME_IN
461 THEN
462 BEGIN { ACME$_PRINCIPAL_NAME_IN }
463 { }
464 { Choose a canned value. }
465 { }
466 CASE PRINCIPAL_INDEX OF
467 PRINCIPAL_1:
468 SET_BUFFER ( 'KIOSKUSER_1' );
469 PRINCIPAL_2:
470 SET_BUFFER ( 'KIOSKUSER_2' );
471 PRINCIPAL_3:
472 SET_BUFFER ( 'KIOSKUSER_3' );
473 PRINCIPAL_4:
474 SET_BUFFER ( 'KIOSKUSER_4' );
475 PRINCIPAL_5:
476 SET_BUFFER ( 'KIOSKUSER_5' );
477 PRINCIPAL_6:
478 SET_BUFFER ( 'KIOSKUSER_6' );
479 PRINCIPAL_7:
480 SET_BUFFER ( 'KIOSKUSER_7' );
481 PRINCIPAL_8:
482 SET_BUFFER ( 'KIOSKUSER_8' );
483 PRINCIPAL_9:
484 SET_BUFFER ( 'KIOSKUSER_9' );
485 PRINCIPAL_10:
486 SET_BUFFER ( 'KIOSKUSER_10' );
487 OTHERWISE
488 { }
489 { There is a bug in this program.}
490 { }
491 RETURN SS$_BUGCHECK;
492 { }
493 END; { CASE PRINCIPAL_INDEX }
494 END { ACME$_PRINCIPAL_NAME_IN }
495 ELSE
496 BEGIN { Item Code is for text }
497 { }
498 { Perhaps the channel is open }
499 { from a previous dialogue step. }
500 { }
501 IF TERMINAL_CHANNEL = 0
502 THEN
503 BEGIN { a channel must be assigned }
504 MY_STATUS :=
505 $ASSIGN (
506 DEVNAM := 'SYS$INPUT',
507 CHAN := TERMINAL_CHANNEL );
508 IF NOT ODD(MY_STATUS)
509 then
510 LIB$SIGNAL(MY_STATUS);
511 END; { a channel must be assigned }
512 { }
513 {We honor SYS$ACM specification of }
514 {Noecho, but because this client }
515 { software only has to work with }
516 { a limited number of hardware }
517 { configurations, we do not bother }
518 { to support Local Echo terminals }
519 { by masking Noecho values the way }
520 { LOGINOUT does. If we chose to }
521 { do that, we could support longer }
522 { input strings than the limit }
523 { LOGINOUT imposes because LOGINOUT }
524 { must fit the prompt and the }
525 {masking into a 255-character }
526 { maximum length imposed by RMS, }
527 { whereas we are using QIO directly. }
528 { }
529 IF ITEMSET_ARRAY^[EACH_ITEMSET]
530 .ACMEIS$L_FLAGS.ACMEDLOGFLG$V_NOECHO
531 THEN
532 QIO_FUNC := IO$_READPROMPT
533 + IO$M_NOECHO
534 ELSE
535 QIO_FUNC := IO$_READPROMPT;
536 MY_STATUS :=
537 $QIOW (
538 EFN := EFN$C_ENF,
539 CHAN := TERMINAL_CHANNEL,
540 FUNC := QIO_FUNC,
541 IOSB := INPUT_IOSB,
542 P1 := INPUT_BUFFER,
543 P2 := SIZE(INPUT_BUFFER),
544 P5 := IADDRESS(CHAR_ARRAY_POINTER_1^),
545 P6 := CHAR_ARRAY_LENGTH_1 );
546 IF ODD(MY_STATUS)
547 THEN
548 MY_STATUS := INPUT_IOSB.IOSB$W_STATUS;
549 IF NOT ODD(MY_STATUS)
550 THEN
551 RETURN MY_STATUS;
552 CONFIRM_IOSB.IOSB$W_BCNT := 0;
553 IF CHAR_ARRAY_POINTER_2 <> NIL
554 THEN
555 REPEAT
556 BEGIN { Confirmation Specified }
557 MY_STATUS :=
558 $QIOW (
559 EFN := EFN$C_ENF,
560 CHAN := TERMINAL_CHANNEL,
561 FUNC := QIO_FUNC,
562 IOSB := CONFIRM_IOSB,
563 P1 := CONFIRM_BUFFER,
564 P2 := SIZE(CONFIRM_BUFFER),
565 P5 := IADDRESS(CHAR_ARRAY_POINTER_2^),
566 P6 := CHAR_ARRAY_LENGTH_2 );
567 IF ODD(MY_STATUS)
568 THEN
569 MY_STATUS := INPUT_IOSB.IOSB$W_STATUS;
570 IF NOT ODD(MY_STATUS)
571 THEN
572 RETURN MY_STATUS;
573 END { Confirmation Specified }
574 UNTIL SUBSTR(CONFIRM_BUFFER,1,
575 CONFIRM_IOSB.IOSB$W_BCNT)
576 = SUBSTR(INPUT_BUFFER,1,
577 INPUT_IOSB.IOSB$W_BCNT);
578 END { Item Code is for text }
579 ELSE
580 { }
581 { Only ACME-specific itemsets }
582 { can have non-text item codes. }
583 { }
584 RETURN SS$_BUGCHECK;
585 { }
586 END; { general or text input itemset }
587 { }
588 { Fill in the Item List with the }
589 { input we just gathered. }
590 { }
591 { Bubble the null terminator up by 1.}
592 { }
593 ITEM_LIST[EACH_ITEM+1] :=
594 ITEM_LIST[EACH_ITEM];
595 { }
596 { Add the new entry. }
597 { }
598 ITEM_LIST[EACH_ITEM].ILE3$W_LENGTH :=
599 INPUT_IOSB.IOSB$W_BCNT;
600 ITEM_LIST[EACH_ITEM].ILE3$W_CODE::ACMEIC$TYPE :=
601 ITEMSET_ARRAY^[EACH_ITEMSET].ACMEIS$W_ITEM_CODE;
602 ITEM_LIST[EACH_ITEM].ILE3$PS_BUFADDR :=
603 IADDRESS(INPUT_BUFFER);
604 EACH_ITEM := EACH_ITEM + 1;
605 EACH_ITEMSET := EACH_ITEMSET + 1;
606 { }
607 END; { process Input Itemset }
608 { }
609 { Process Output Itemsets up to the next }
610 { Input Itemset. }
611 { }
612 WHILE EACH_ITEMSET <= ITEMSET_COUNT DO
613 BEGIN { process one itemset }
614 CHAR_ARRAY_LENGTH_1
615 := ITEMSET_ARRAY^[EACH_ITEMSET]
616 .acmeis$q_data_1
617 .L0 MOD 65536;
618 CHAR_ARRAY_POINTER_1
619 := ITEMSET_ARRAY^[EACH_ITEMSET]
620 .acmeis$q_data_1
621 .L1::CHAR_ARRAY_TYPE_POINTER;
622 CHAR_ARRAY_LENGTH_2
623 := ITEMSET_ARRAY^[EACH_ITEMSET]
624 .acmeis$q_data_2
625 .L0 MOD 65536;
626 CHAR_ARRAY_POINTER_2
627 := ITEMSET_ARRAY^[EACH_ITEMSET]
628 .acmeis$q_data_2
629 .L1::CHAR_ARRAY_TYPE_POINTER;
630 IF ITEMSET_ARRAY^[EACH_ITEMSET].ACMEIS$L_FLAGS
631 .ACMEDLOGFLG$V_INPUT
632 THEN
633 { }
634 { Recurse to provide an input buffer }
635 { for this input itemset. }
636 { }
637 RETURN RECURSE_OVER_ITEMS (
638 ITEMSET_ARRAY^[EACH_ITEMSET]
639 .ACMEIS$W_MAX_LENGTH )
640 { }
641 ELSE
642 IF ITEMSET_ARRAY^[EACH_ITEMSET].ACMEIS$W_MSG_TYPE
643 .ACMEMC$V_ACME_SPECIFIC
644 AND NOT ITEMSET_ARRAY^[EACH_ITEMSET]
645 .ACMEIS$W_ITEM_CODE.ACMEIC$V_UCS
646 THEN { ACME-specific non-text }
647 { }
648 { Comparing MY_CONTXT^.ACMECB$L_ACME_ID }
649 { .ACMEID$V_ACME_NUM field against the }
650 { (previously queried) IDs of ACMEs from }
651 { which this client expects ACME-specific}
652 { output itemsets, and also }
653 { }
654 { comparing ITEMSET_ARRAY^[EACH_ITEMSET] }
655 { .ACMEIS$W_MSG_TYPE.ACMEMC$W_MSG_CODE}
656 { against the 16-bit values of expected }
657 { ACME-specific message types, we get the}
658 { information to dispatch to handle each }
659 { of the ACME-specific message types that}
660 { this client program knows about. }
661 { }
662 { But this client does not know about any}
663 { ACME-specific message types, so an ACME}
664 { that sent a message we cannot handle is}
665 { behaving totally incorrectly, and we }
666 { give up. }
667 { }
668 ASSERT(FALSE,
669 'unknown ACME-specific message type')
670 { }
671 ELSE
672 BEGIN { text or general output itemset }
673 { }
674 { Pascal does not give us the ability }
675 { that more strongly typed languages do }
676 { to force a compile-time failure in the }
677 { case where new message types have been }
678 { added to a subsequent release of VMS, }
679 { so we make these run-time checks. }
680 { }
681 ASSERT(ACMEMC$K_MIN_GEN_MSG
682 = ACMEMC$K_GENERAL,
683 'ACMEMC$K_MIN_GEN_MSG has shifted');
684 ASSERT(ACMEMC$K_MAX_GEN_MSG
685 = ACMEMC$K_DIALOGUE_ALERT,
686 'ACMEMC$K_MAX_GEN_MSG has shifted');
687 ASSERT(ACMEMC$K_MIN_LOGON_MSG
688 = ACMEMC$K_SYSTEM_IDENTIFICATION,
689 'ACMEMC$K_MIN_LOGON_MSG has shifted');
690 ASSERT(ACMEMC$K_MAX_LOGON_MSG
691 = ACMEMC$K_MAIL_NOTICES,
692 'ACMEMC$K_MAX_LOGON_MSG has shifted');
693 { }
694 { All general output itemsets carry text,}
695 { but based on the type of item, it would}
696 { be possible to display them on various }
697 { parts of the screen with distinctive }
698 { colors and video characteristics. }
699 { }
700 { That part is left as an exercise for }
701 { the reader, and in each case we call }
702 { WRITE_ITEM_PLAIN. }
703 { }
704 CASE ITEMSET_ARRAY^[EACH_ITEMSET]
705 .ACMEIS$W_MSG_TYPE
706 .ACMEMC$W_MSG_CODE of
707 ACMEMC$K_GENERAL :
708 { General text }
709 WRITE_ITEM_PLAIN;
710 ACMEMC$K_HEADER :
711 { Header text }
712 WRITE_ITEM_PLAIN;
713 ACMEMC$K_TRAILER :
714 { Trailer text }
715 WRITE_ITEM_PLAIN;
716 ACMEMC$K_SELECTION :
717 { Acceptable choices }
718 WRITE_ITEM_PLAIN;
719 ACMEMC$K_DIALOGUE_ALERT :
720 { Alert (advisory) }
721 WRITE_ITEM_PLAIN;
722 ACMEMC$K_SYSTEM_IDENTIFICATION :
723 { System identification text }
724 WRITE_ITEM_PLAIN;
725 ACMEMC$K_SYSTEM_NOTICES :
726 { System notices }
727 WRITE_ITEM_PLAIN;
728 ACMEMC$K_WELCOME_NOTICES :
729 { Welcome notices, }
730 WRITE_ITEM_PLAIN;
731 ACMEMC$K_LOGON_NOTICES :
732 { Logon notices }
733 WRITE_ITEM_PLAIN;
734 ACMEMC$K_PASSWORD_NOTICES :
735 { Password notices }
736 WRITE_ITEM_PLAIN;
737 ACMEMC$K_MAIL_NOTICES :
738 { MAIL notices }
739 WRITE_ITEM_PLAIN;
740 otherwise
741 { }
742 { Some other output message type.}
743 { }
744 WRITE_ITEM_PLAIN;
745 { }
746 END; { CASE ACMEMC$W_MSG_CODE }
747 END; { text or general output itemset }
748 EACH_ITEMSET := EACH_ITEMSET + 1;
749 END; { process one itemset }
750 { }
751 { We have reached the end, call SYS$ACM. }
752 { }
753 RECURSE_OVER_ITEMS := $ACM (
754 EFN := EFN$C_ENF,
755 FUNC := ACME$_FC_AUTHENTICATE_PRINCIPAL,
756 ITMLST := ITEM_LIST,
757 CONTXT := %IMMED IADDRESS(MY_CONTXT),
758 ACMSB := MY_ACMESB );
759 END; { FUNCTION RECURSE_OVER_ITEMS }
760 BEGIN { FUNCTION RESPOND }
761 ITEM_LIST[EACH_ITEM].ILE3$W_LENGTH := 0;
762 ITEM_LIST[EACH_ITEM].ILE3$W_CODE := 0;
763 ITEM_LIST[EACH_ITEM].ILE3$PS_BUFADDR := 0;
764 ITEM_LIST[EACH_ITEM].ILE3$PS_RETLEN_ADDR := NIL;
765 { }
766 { We provide 0 as an indication that this is the }
767 { outermost call, rather than one made due to }
768 { encountering an input itemset. }
769 { }
770 RESPOND := RECURSE_OVER_ITEMS ( 0 );
771 { }
772 END; { FUNCTION RESPOND }
773 BEGIN { FUNCTION AUTHENTICATE }
774 { }
775 { Make an initial query to determine the ACME ID of }
776 { the Fingerprint ACME in the current running system. }
777 { }
778 ACM_QUERY_ITMLST[0].ILE3$PS_BUFADDR := IADDRESS(SYS$ACM_ACME_ID);
779 ACM_QUERY_ITMLST[1].ILE3$PS_BUFADDR := IADDRESS(ACME_QUERY_ACME_NAME);
780 ACM_QUERY_ITMLST[2].ILE3$PS_BUFADDR := IADDRESS(FINGERPRINT_ACME_NAME);
781 ACM_QUERY_ITMLST[3].ILE3$PS_BUFADDR := IADDRESS(ACME_TARGET_DOI_ID);
782 ACM_QUERY_ITMLST[4].ILE3$PS_BUFADDR := IADDRESS(FINGERPRINT_ACME_ID);
783 MY_STATUS:=1;
784 MY_ACMESB.ACMESB$L_STATUS := ACME$_NOSUCHDOI;
785 IF not ODD(MY_STATUS) then
786 MY_STATUS := $ACMW (
787 EFN := EFN$C_ENF,
788 FUNC := ACME$_FC_QUERY,
789 ITMLST := ACM_QUERY_ITMLST,
790 ACMSB := MY_ACMESB );
791 IF ODD(MY_STATUS)
792 then
793 MY_STATUS := MY_ACMESB.ACMESB$L_STATUS;
794 IF NOT ODD(MY_STATUS)
795 then
796 { }
797 { "No Fingerprint ACME present" is a perfectly valid }
798 { state of affairs, and we record a zero ACME ID. }
799 { }
800 IF MY_STATUS = ACME$_NOSUCHDOI
801 THEN
802 FINGERPRINT_ACME_ID := ZERO
803 ELSE
804 LIB$SIGNAL(MY_STATUS);
805 { }
806 { Make an initial authentication call. }
807 { }
808 MY_CONTXT := (-1)::ACMECB_PTR;
809 MY_ACM_ITMLST_A[0].ILE3$PS_BUFADDR := IADDRESS(MY_LOGON_TYPE);
810 MY_ACM_ITMLST_A[1].ILE3$PS_BUFADDR := IADDRESS(MY_DIALOGUE_SUPPORT);
811 MY_STATUS := $ACMW (
812 EFN := EFN$C_ENF,
813 FUNC := ACME$_FC_AUTHENTICATE_PRINCIPAL,
814 ITMLST := MY_ACM_ITMLST_A,
815 CONTXT := %IMMED IADDRESS(MY_CONTXT),
816 ACMSB := MY_ACMESB );
817 IF ODD(MY_STATUS)
818 then
819 MY_STATUS := MY_ACMESB.ACMESB$L_STATUS;
820 IF NOT ODD(MY_STATUS)
821 then
822 { }
823 { "Operation Incomplete" is to be expected. }
824 { }
825 IF MY_STATUS <> ACME$_OPINCOMPL
826 THEN
827 LIB$SIGNAL(MY_STATUS);
828 { }
829 { Respond to successive dialogue steps. }
830 { }
831 WHILE MY_STATUS = ACME$_OPINCOMPL DO
832 BEGIN
833 ITEMSET_ARRAY := MY_CONTXT^
834 .acmecb$ps_item_set::ITEMSET_ARRAY_TYPE_POINTER;
835 MY_STATUS
836 := RESPOND ( MY_CONTXT^.acmecb$l_item_set_count );
837 IF NOT ODD(MY_STATUS)
838 then
839 BEGIN { Abandon the authentication }
840 MY_ACM_ITMLST_A[0].ILE3$W_LENGTH := 0;
841 MY_ACM_ITMLST_A[0].ILE3$W_CODE := 0;
842 MY_ACM_ITMLST_A[0].ILE3$PS_BUFADDR := 0;
843 MY_ACM_ITMLST_A[0].ILE3$PS_RETLEN_ADDR := NIL;
844 TRASH_STATUS := $ACMW (
845 EFN := EFN$C_ENF,
846 FUNC := ACME$_FC_FREE_CONTEXT,
847 ITMLST := MY_ACM_ITMLST_A,
848 CONTXT := %IMMED IADDRESS(MY_CONTXT),
849 ACMSB := MY_ACMESB );
850 LIB$SIGNAL(MY_STATUS);
851 END; { Abandon the authentication }
852 MY_STATUS := $SYNCH_ACMESB (
853 EFN := EFN$C_ENF,
854 IOSB := MY_ACMESB );
855 IF ODD(MY_STATUS)
856 then
857 MY_STATUS := MY_ACMESB.ACMESB$L_STATUS;
858 END;
859 IF NOT ODD(MY_STATUS)
860 then
861 LIB$SIGNAL(MY_STATUS);
862 { }
863 IF FINGERPRINT_READER_CHANNEL <> 0
864 THEN
865 BEGIN { a channel was assigned }
866 MY_STATUS :=
867 $DASSGN (
868 CHAN := FINGERPRINT_READER_CHANNEL );
869 IF NOT ODD(MY_STATUS)
870 then
871 LIB$SIGNAL(MY_STATUS);
872 END; { a channel was assigned }
873 { }
874 IF TERMINAL_CHANNEL <> 0
875 THEN
876 BEGIN { a channel was assigned }
877 MY_STATUS :=
878 $DASSGN (
879 CHAN := TERMINAL_CHANNEL );
880 IF NOT ODD(MY_STATUS)
881 then
882 LIB$SIGNAL(MY_STATUS);
883 END; { a channel was assigned }
884 { }
885 AUTHENTICATE := TRUE;
886 END; { FUNCTION AUTHENTICATE }
887 BEGIN { PROGRAM ACM_SHOPFLOOR }
888 AUTHENTICATE ( PRINCIPAL_10 );
889 END. { PROGRAM ACM_SHOPFLOOR }
|