1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
|
! cgi_protocol.f90 --
! Module for interfacing with a web server via the CGI protocol
! (GET, POST, ...)
!
! TODO:
! - find out how to deal with text area data
! - find out how to deal with uploaded files
! - merge environment variables into the cgi_get routines
! - implement the SCGI protocol
! - implement delayed responses (also via customisable template)
!
module cgi_protocol
implicit none
integer, parameter :: DICT_KEY_LENGTH = 80
integer, parameter :: DICT_VALUE_LENGTH = 200
integer, parameter :: DICT_BUFFER_LENGTH = DICT_KEY_LENGTH + DICT_VALUE_LENGTH + 1
integer, parameter :: output_no_header = 0
integer, parameter :: output_html = 1
integer, parameter :: output_text = 2
integer, parameter, private :: output_html_delayed = 3 ! Not implemented yet!
integer, parameter, private :: output_text_delayed = 4
type DICT_DATA
character(len=DICT_VALUE_LENGTH) :: value
end type DICT_DATA
interface cgi_get
module procedure cgi_get_string
module procedure cgi_get_integer
module procedure cgi_get_real
end interface
type(DICT_DATA), parameter :: dict_null = dict_data('')
integer, private, save :: method = -1
integer, private, save :: luout_cgi = -1
logical, private, save :: header_written
!
! Body of source code for storing and retrieving the data
! (also contains the CONTAINS clause)
!
include 'dictionary.f90'
! cgi_begin --
! Determine the type of interaction with the server and retrieve the data
!
! Arguments:
! html Whether the output will be HTML or plain text
! dict Dictionary holding the information from the server (output)
! luout LU-number for writing the file (output!)
!
! Note:
! This routine determines the way the server passes on the data
! by looking for clues in the environment variables QUERY_LENGTH
! and QUERY_STRING
!
!
! TODO:
! Support for two-pass run (if the computation takes a longer
! than a few seconds)
!
subroutine cgi_begin( html, dict, luout )
integer, intent(in) :: html
type(DICT_STRUCT), pointer :: dict
integer, intent(out) :: luout
integer :: length
integer :: status
logical :: opend
character(len=DICT_BUFFER_LENGTH) :: string
character(len=1) :: ch
!
! Clean up, if necessary
!
if ( associated(dict) ) then
call dict_destroy( dict )
endif
header_written = .false.
!
! Determine which input method
!
call get_environment_variable( "QUERY_STRING", length=length, status=status )
if ( status == 0 ) then
call cgi_get_method( dict, length )
method = 1
else
call get_environment_variable( "CONTENT_LENGTH", value=string, status=status )
if ( status == 0 ) then
read( string, * ) length
call cgi_post_method( dict, length )
method = 1
else
read( *, '(A)', advance = 'no' ) ch
if ( ch == '%' ) then
!
! TODO: better method for determining length
call cgi_dustmote_method( dict )
method = 2
elseif ( index( '1234567890', ch ) > 0 ) then
! call cgi_simple_cgi( dict )
method = -1
else
method = -1
endif
endif
endif
!
! If we did not get the correct information, just blow the
! whole thing off
!
if ( method == -1 ) then
call cgi_error( "CGI protocol not recognised or not implemented" )
endif
!
! What LU-number for the output
! method 1: write directly to standard output (assumed to be at unit 6)
! method 2: write to a file first (cgiout)
!
if ( method == 1 ) then
luout = 6
endif
if ( method == 2 ) then
do luout = 10,99
inquire( luout, opened = opend )
if ( .not. opend ) then
exit
endif
enddo
open( luout, file = 'cgiout' )
endif
luout_cgi = luout
!
! Write the header lines
!
select case ( html )
case ( output_html, output_text )
call cgi_header( html )
case( output_html_delayed, output_text_delayed )
! TODO
case( output_no_header )
! Writing the header is delayed, because the type is not known yet
case default
call cgi_error( "Programming error: wrong value for parameter 'html' in CGI_BEGIN" )
end select
end subroutine cgi_begin
! cgi_header --
! Write the CGI header information
!
! Arguments:
! type Type of header
!
subroutine cgi_header( type )
integer, intent(in) :: type
header_written = .true.
select case ( type )
case ( output_html, output_html_delayed )
write( luout_cgi, '(a)' ) 'Content-Type: text/html;charset=iso8859-1'
write( luout_cgi, '(a)' ) ''
case( output_text, output_text_delayed )
write( luout_cgi, '(a)' ) 'Content-Type: text/plain;charset=iso8859-1'
write( luout_cgi, '(a)' ) ''
case( output_no_header )
call cgi_error( "Programming error: value 'output_no_header' not allowed in CGI_HEADER" )
case default
call cgi_error( "Programming error: wrong value for parameter 'type' in CGI_HEADER" )
end select
end subroutine cgi_header
! cgi_get_method --
! Get the information via the environment variable QUERY_STRING
!
! Arguments:
! dict Dictionary holding the information from the server (output)
! length Total length of the input
!
subroutine cgi_get_method( dict, length )
type(DICT_STRUCT), pointer :: dict
integer, intent(in) :: length
character(len=length) :: buffer
call get_environment_variable( "QUERY_STRING", value=buffer )
call cgi_store_dict( dict, buffer )
end subroutine cgi_get_method
! cgi_post_method --
! Get the information via standard input
!
! Arguments:
! dict Dictionary holding the information from the server (output)
! length Total length of the input
!
subroutine cgi_post_method( dict, length )
type(DICT_STRUCT), pointer :: dict
integer, intent(in) :: length
character(len=length) :: buffer
read( *, '(a)', advance='no' ) buffer
call cgi_store_dict( dict, buffer )
end subroutine cgi_post_method
! cgi_dustmote_method --
! Get the information line by line
!
! Arguments:
! dict Dictionary holding the information from the server (output)
!
!
subroutine cgi_dustmote_method( dict )
type(DICT_STRUCT), pointer :: dict
type(DICT_DATA) :: data
character(len=DICT_KEY_LENGTH) :: key
character(len=DICT_BUFFER_LENGTH) :: input
integer :: k
integer :: lu
integer :: ierr
logical :: opend
read( *, '(a)' ) input ! Skip the remainder of the first line
read( *, '(a)' ) input
do while ( input /= '%END%' )
call cgi_store_dict( dict, input )
read( *, '(a)', iostat=ierr ) input
if ( ierr /= 0 ) then
exit
endif
enddo
end subroutine cgi_dustmote_method
! cgi_store_dict --
! Store the information in the dictionary
!
! Arguments:
! dict Dictionary holding all information
! string Complete string received from CGI server
!
subroutine cgi_store_dict( dict, string )
type(DICT_STRUCT), pointer :: dict
character(len=*), intent(in) :: string
character(len=DICT_KEY_LENGTH) :: key
character(len=len(string)) :: buffer
type(DICT_DATA) :: data
integer :: k
integer :: keq
buffer = string
do
k = index( buffer, '&' )
if ( k .le. 0 ) then
if ( buffer == ' ' ) then
exit
else
k = len(buffer) + 1 ! Remaining piece
endif
endif
call cgi_decode_string( buffer(1:k-1) )
!
! Store the string
!
keq = index( buffer(1:k-1), '=' )
if ( keq > 0 ) then
key = buffer(1:keq-1)
data%value = buffer(keq+1:k-1)
if ( .not. associated( dict ) ) then
call dict_create( dict, key, data )
else
call dict_add_key( dict, key, data )
endif
endif
if ( k < len(buffer) ) then
buffer = buffer(k+1:)
else
buffer = ' '
endif
enddo
end subroutine cgi_store_dict
! cgi_decode_string --
! Decode the string (replace + and %xx)
!
! Arguments:
! dict Dictionary holding all information
! string Complete string received from CGI server
!
subroutine cgi_decode_string( string )
character(len=*), intent(inout) :: string
integer :: k
integer :: ch
!
! First the +'s
!
do
k = index( string, '+' )
if ( k .le. 0 ) exit
string(k:k) = ' '
enddo
!
! Now %xx
!
do
k = index( string, '%' )
if ( k .le. 0 ) exit
read( string(k+1:k+2), '(z2)' ) ch
string(k:) = achar(ch) // string(k+3:)
enddo
end subroutine cgi_decode_string
! cgi_end --
! Indicate to the server that we are done
! Arguments:
! None
! Note:
! This is simply done by writing a file cgiready,
! if method 2 is used. Stop in all cases
!
subroutine cgi_end
integer :: lu
logical :: opend
if ( method == 2 ) then
do lu = 10,99
inquire( lu, opened=opend )
if ( .not. opend ) then
open( lu, file = "cgiready" )
close( lu )
exit
endif
enddo
endif
stop
end subroutine cgi_end
! cgi_error --
! Report a fatal error
! Arguments:
! msg Message to be printed
! template Template file to be used (optional)
!
subroutine cgi_error( msg, template )
character(len=*), intent(in) :: msg
character(len=*), intent(in), optional :: template
character(len=200) :: text
integer :: k
integer :: ierr
integer :: lu
logical :: opend
logical :: exists
exists = .false.
if ( present(template) ) then
inquire( file = template, exist = exists )
endif
if ( .not. header_written ) then
write( luout_cgi, '(a)' ) 'Content-Type: text/html;charset=iso8859-1'
write( luout_cgi, '(a)' ) ''
endif
if ( exists ) then
do lu = 10,99
inquire( lu, opened = opend )
if ( .not. opend ) then
exit
endif
enddo
open( lu, file = template )
do
read( lu, '(a)', iostat=ierr ) text
if ( ierr /= 0 ) exit
k = index( text, 'MSG' )
if ( k > 0 ) then
write( luout_cgi, '(3a)' ) text(1:k-1), trim(msg), text(k+3:)
else
write( luout_cgi, '(a)' ) text
endif
enddo
close( lu )
else
write( luout_cgi, * ) '<html>'
write( luout_cgi, * ) '<head><title>Severe error</title></head>'
write( luout_cgi, * ) '<body>'
write( luout_cgi, * ) '<b>', trim(msg), '</b>'
write( luout_cgi, * ) '</body></html>'
endif
call cgi_end
end subroutine cgi_error
! cgi_get_session --
! Get the value of the "sessionid" variable
! Arguments:
! dict Dictionary with values
! value Value of the session ID (character(len=20))
! Note:
! The session ID can be used to uniquely identify the
! connection with the user. But it should be passed into the
! HTML output as a hidden variable (see the documentation
! for more information)
!
subroutine cgi_get_session( dict, value )
type(DICT_STRUCT), pointer :: dict
character(len=*) :: value
character(len=20) :: time_string
type(DICT_DATA) :: data
if ( dict_has_key( dict, "sessionid" ) ) then
data = dict_get_key( dict, "sessionid" )
value = data%value
else
call date_and_time( time = time_string )
value = time_string(5:6) // time_string(8:10)
data%value = value
call dict_add_key( dict, "sessionid", data )
endif
end subroutine cgi_get_session
! cgi_get_* --
! Get the value of variables
! Arguments:
! dict Dictionary with values
! varname Name of the variable to retrieve
! value Value of the variable
! Note:
! If the variable does not exist, then the value
! is not changed! (Use dict_has_key() to check the
! existence)
!
subroutine cgi_get_string( dict, varname, value )
type(DICT_STRUCT), pointer :: dict
character(len=*) :: varname
character(len=*) :: value
type(DICT_DATA) :: data
if ( dict_has_key( dict, varname ) ) then
data = dict_get_key( dict, varname )
value = data%value
endif
end subroutine cgi_get_string
subroutine cgi_get_integer( dict, varname, value )
type(DICT_STRUCT), pointer :: dict
character(len=*) :: varname
integer :: value
type(DICT_DATA) :: data
integer :: ierr
integer :: new_value
if ( dict_has_key( dict, varname ) ) then
data = dict_get_key( dict, varname )
read( data%value, *, iostat=ierr ) new_value
if ( ierr == 0 ) then
value = new_value
endif
endif
end subroutine cgi_get_integer
subroutine cgi_get_real( dict, varname, value )
type(DICT_STRUCT), pointer :: dict
character(len=*) :: varname
real :: value
type(DICT_DATA) :: data
integer :: ierr
real :: new_value
if ( dict_has_key( dict, varname ) ) then
data = dict_get_key( dict, varname )
read( data%value, *, iostat=ierr ) new_value
if ( ierr == 0 ) then
value = new_value
endif
endif
end subroutine cgi_get_real
subroutine cgi_get_logical( dict, varname, value )
type(DICT_STRUCT), pointer :: dict
character(len=*) :: varname
logical :: value
type(DICT_DATA) :: data
integer :: ierr
integer :: new_value
if ( dict_has_key( dict, varname ) ) then
data = dict_get_key( dict, varname )
read( data%value, *, iostat=ierr ) new_value
if ( ierr == 0 ) then
value = (new_value == 1)
endif
endif
end subroutine cgi_get_logical
end module cgi_protocol
|