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