: (4) : (1) (3) (3) (6) (1) : (3) (1)
: (3) : (1) (3) (1) : (3) : (1) (3) (2) :
- 66 out of 78 -
(48) (49) (50) (51) (52) (53) (54)
(55)
:
(56)
:
(57)
: : : : :
(58) (59) (60) (61)
(1) (1) (1) (1) (1) (1) (1) (6) (1) (14) (1) (6) (1) (1) (4) (1) (1)
: : : : : : :
(3) (3) (3) (3) (3) (3) (3) (3) (3) (2) (3) (3) (3) (3) (3) (3)
This just shows you all the possibilities of configuration you have. Don’t hesitate to try them... it’s the best way to learn how to use them.
A word about modularity When I was writing my code I always keep as a goal the code to be maintainable, extensive and modular. We talked quickly earlier of the ability given to the administrator to add new functionalities. I will now show you how to add a new functionality to the system... The processing of the <meta> tag. Our goal will be to write the author Name in a nice sentence at the end of the document. #!/perl/bin/perl #meta.cgi - Process the meta tag. sub run_meta { my $ruserPref = $_[0] ; } return 1 ; The meta.cgi file in the rules directory.
The meta tag in the rules.xml.
The meta tag in the preferences.xml of the user.
First, we will modify the rules.xml file to tell the system that it has to perform an action for the <meta> tag and the very end of the processing. The meta tag in the rules.xml.
To do this, we have to add an end attribute to the tag and put its value to true
- 67 out of 78 -
(1)
.
If the tag we would have wanted to process would have been a “closed” tag (meaning a tag that both need an opening and a closing tag), its type would have been closed and it would have had another attribute called run that specify when the tag has to be processed. The correct values for run is either before (only before) or before-after (before and after). Now we need want the user to be able to specify how the sentence introducing the author name should be constructed. For this we modify the user’s preference file. Actually this is absolutely not compulsory but it will enable us to later show how our Perl “mini-program” can interact with the user’s preference file. This document was created by %%AUTHOR%%
(1) (2) (3)
The meta tag in the preferences.xml of the user. (1)
(2)
(3)
We first need to activate the processing of that tag but setting the action attribute to on. Then we create an tag to restrict the usage of the specified text to a <meta> tag that specifies author information. The %%AUTHOR%% keyword will be replace by the real author name by the system. I used a similar system for other tags like the tag.
Now let us attack the Perl script. I don’t want to teach you Perl and you already most probably know it. I will only explain the part that deals with <meta> tag or accessing the user preference file. #!/perl/bin/perl #meta.cgi - Process the meta tag. sub run_meta { my $ruserPref = $_[0] ; my $fullTag = $_[1] ; my $authorString = $ruserPref->{author} ; my $author = findAuthor($fullTag) ; if ($autor) { $authorString =~ s/%%AUTHOR%%/$author/i ; $globalTagVariables{meta}{author} = $authorString ; }
(1) (2)
(3) (4)
(5)
(6)
return 1 ; } sub run_meta_end { $tmpString = $globalTagVariables{meta}{author} ; if ($tmpString) { return $tmpString ; } else { return 1 ; } }
- 68 out of 78 -
(7) (8)
(9)
sub findAuthor { my $fullTag = $_[0] ;
(4)
$fullTag =~ m!\bname\s*=\s*(?:"([^"]*)"|'([^']*)'|([^'">\s]+))!i ; my $type = $+ ; if ($type eq "author") { $fullTag =~ m!\bcontent\s*=\s*(?:"([^"]*)"|'([^']*)'|([^'">\s]+))!i ; return $+ ; } else { return 0 ; } } return 1 ; The meta.cgi file in the rules directory. (1) (2)
(3) (4)
(5) (6)
(7) (8) (9)
This function will be called each time a <meta> tag is processed. $ruserPref holds the user’s preference for that particular tag. $fullName holds the full tag string (for example <meta name="author" content="Pierre Naquin">). $authorString is getting the sentence that is in the user’s preference file. $author is getting either 0 (if the <meta> tag is not giving information about the author) or the author name. We are replacing the %%AUTHOR%% by the author name in $authorString. We save the author sentence in a global variable; we will therefore be able to recover its value at the end to add it to the document. Every tag has a special emplacement to put all its global variables: $globalTagVariables{tagName}{whateverIwant}. The system has finished processed all the tags, now it called all “ends” functions. I recover the saved string in a temporary variable: $tmpString. By returning it, it is added to the document! WE HAVE FINISHED!
Conclusion In this section we saw how the system is organised, how it can be configured and how it is modular and maintainable. We even actually added a new functionality to it in only two pages! A lot of organisation work and coding work were done to obtain such functionalities. My point of view is that it is better for a system to be able to add new functionalities easily, to make it evolve easily that to have a lot of operations built-in from the beginning. Of course the best is to have both the ability of evolution and a lot of functionalities accessible as soon as you open the box; that is what I tried to do on this work!
- 69 out of 78 -
Conclusion - 70 out of 78 -
This dissertation has been a conscious effort towards the development of the VoiceXML Web Browser using Perl as the scripting language and VoiceXML (and its consorts) as an output language. During this dissertation we tried to focus on both the technical aspect and the user interface aspect. Both aspects were interesting but in some different ways: technically it was very challenging to try to build a very advance system on such a new technology as VoiceXML. On the other hand working on voice user interfaces let you think about interfaces right from scratch as there are not yet any “standard rules” or interfaces known as being efficient or good. We came through the documentation problem we faced when dealing with some advance computing subject (writing a HTML browser is quite an advance subject!) and when it is about very new technologies, standard or areas of computing (voice interfaces for example). Then we went through all the processing that a web browser (like Mozilla, Firefox or Internet Explorer) is doing but we transposed it to the web server. We made our brain work to try to find intelligent ways of transforming visual content into audible content. I really do think this area of research is very valuable (commercially and intellectually); not only for helping disable people but also to make working with computer always more efficient; meaning performing several task at the same time. We also had the sadness of not being able to test our project. To solve this I really hope the W3C project of developing a free of charge implementation of a VoiceXML Interpreter will one day terminate. This would bring lots of new developers to the creation of telephonic services bringing the Internet boom back to phones! My only hope is that it will not lay off all these nice call-centres ladies with their so charming voices... “Would you please wait? ... and see!”
- 71 out of 78 -
Recommendations
- 72 out of 78 -
Introduction We spent quite some time together regarding how VoiceXML could be push into its limit to make a great piece of software. The idea of making a voice browser using VoiceXML – I do believe – is a natural and unnatural. Natural because with VoiceXML, we skill all the problem of voice recognition and voice synthesis to focus ourselves of the HTML transformation. Unnatural because first of all its nature that is to provide a dialog between a user and a computer and because of all the problems we mention earlier (for example the noncapacity of recognising an arbitrary test). Instead of a real dialog between the computer and the user, we built a “dialog” totally driven by the user. The user decides when he wants to perform an action, what action he/she wants to perform and how this action should be performed without consulting the computer. We saw why modularity is not a plus but a must have in the system and how this modularity can be used to add new functionalities. Before leaving to some so much less enjoyable activities (!); I would like to describe you some of the ideas I had to make the system even better. I would have implemented them myself if I had more time, but I prefer to give you all the keys to continue this project if you fill like. These ideas are simple to implement and if you globally understood how everything works, you should be fine for implementing them very quickly. I hope you enjoyed reading this dissertation. It is not only my work by a symbol of my believe that technologies should first be driven by ideas.
Now it is your turn to work! I’ll try to describe in the best way I can some ideas that could be implemented to make the VoiceXML Web Browser even more user-friendly and efficient. •
Testing in real case
The first and the far more important thing is to be able to test in real case the system. It will highlight what parts are working well and what parts are putting the user in trouble and should be specially taken care of. Doing so will also make us realise some bugs that have escape from my reading and re-reading and re-re-reading of VoiceXML code. •
A graphical user-interface for configuration
Both users and administrators would really appreciate a graphical interface for making their every day jobs in configuring the system. I can say this with no doubt because I damn myself so much so many times not have done that! Building a graphical interface will also make simple some complex rules edition like the ones that are managing images. •
A more efficient tag auto-closing system
Even if the system already does a really good job in auto-closing some tags that are left open by the very naughty HTML designer, it has to be improved a little. - 73 out of 78 -
As it is now, an opened tag can only be automatically closed if another tag (of the same type or not) is opened. The system should also be configurable for autoclosing when some other tag is closed: - element 1
- element 2
- element 3
an - tag is opened an
- tag is closed. an
- tag is opened an
- tag is closed. an
- tag is opened
- element 1 again
- element 2 again
- element 3 again
an - tag is closed. an
- tag is opened an
- tag is closed. an
- tag is opened an
- tag is closed. an
- tag is opened
[...]
An - tag is automatically closed by the opening a new one.
The last - tag of the first sequence is not closed when it should and the last
- tag is never closed.
an an an an
- element 1
- element 2
- element 3
- tag is opened
- tag is closed. an
- tag is opened
- tag is closed. an
- tag is opened
- tag is closed.
An - tag is automatically closed by the opening a
- tag or the closure of a
tag.
With such a rule the - tag is now closed when it should. •
Skipping the content of a tag
The system should be able to skip the content of some tags. Right now, the system can not perform any action for certain tags but cannot make their content to be skipped. A very good example of such a tag is the <script> tag. We specially don’t want the system to try to explain JavaScript to the user. Actually the system is still working fine in the case of a <script> tag is the HTML developer did his/her job correctly: if he/she had put the JavaScript code into comments the system, the JavaScript will be skipped; but unfortunately most developer don’t care... so we have to care for them. •
Processing <meta> tags
Meta tags are full of information that can be very useful to help the user to understand the content of a page. We already gave an example of interesting processing of the <meta> tag to illustrate how easy it was to write our own module for processing a particular tag. The processing of the <meta> tags should be generalised for providing valuable information. •
Titling windows
- 74 out of 78 -
I decided for this first version of the system not to give titles to windows because of the fact that lots of sites have titles that doesn’t mean anything (like “-------> THE BEST WEBSITE IN THE WORLD tags could be a great source for titling. •
Skipping advertising images
We are already doing some great processing for images; but these processes can be applied to some images that shouldn’t, like advertisements. I thought of two ways of recognising advertisements images: By checking the size of an image (advertising images usually have specific sizes). More efficient, by checking the location of the image (advertising images are not usually located on the same web server than the web site). These days advertising images have become more and more complex and now almost always imply the use of JavaScript codes. This is good for us as we are not processing JavaScript: therefore most of the very new advertising images won’t disturb our user. •
A better processing
Even after thinking on it hours and hours I could not find a generic solution to the problem of . I still think what we are doing is the best: don’t do anything at all. We could simply enable the user to dynamically change the way the table is processed: row by row or column by column. For this the easiest way is to pre-process both version and enable the user to choose between them by “bargin” what version he/she wants. •
for everyone
The configuration property is only available to the - (a tag used for expressing a definition). A good idea would to extend this property to all the formatting tags (like , , ...); especially because it doesn’t need any work... just some copy/paste! •
Information of demand
Not to give all the information all the time is first a good idea because it makes the system quicker to use but also because some information do not make any sense if provided directly in the middle of the document. I do also think that after some successful request of information, the user start to trust more the system because of the fact that information only given when relevant makes the computer look intelligent. This fact should be verified in a real case.
- 75 out of 78 -
Ideally a user agent should be incorporated into the system. Unfortunatly scripting languages like ECMAScript (the only language available on the client side) are not really efficient with dealing with complex computing like those used for user agents. An implementation idea would be to the same as the one I will quickly describe just now for context help messages. •
Help messages
Providing help to the user is very important for application. It becomes even more critical with voice application as the user can get “lost” very easily. Context help can be added in the same way that we did with “bargin” links or “bargin” fields by using modal fields. Global help can be added as separate fields and rules to answer generic questions that don’t depend on where the user is in the speaking of the file. A good idea would be to build a general interface incorporated into the core of the system letting each tag to add context help just by calling a function. I do not think the built-in functionality can be used for what we are trying to do as this was imagine by the creators of VoiceXML as a source of help in a modal context. •
Recursive numbering for listing tags
Recursive numbering for listing tags was working very fine until the moment I introduced the auto-closing functionally. I was very proud of it but I think the auto-closing ability is far more important regarding to the legions of web sites that don’t close their tags. I even think the HTML 4.0 recommendation from the W3C says that - tags must not be closed. With a little more work and a more precise auto-closing system, the recursive numbering will back very happily! •
The “flat mode” for lists
First of all, element in a list should always be numbered (maybe using letters or others): it cut the sentence and makes the user understand that there are different elements that are described. This can already done using the users’ preference files. Another idea to make long hierarchical list easier to understand to user is to distribute send them in my copyrighted “flat mode”!
- 76 out of 78 -
The normal way of seeing it: the “tree mode”.
Another way of seeing it: the “flat mode”.
In the “flat mode”, gears are expressed one by one instead of recursively go to the end of the first element to continue like in the “tree mode”. Processing by gears enables the user to first have the global idea and then continues in refining each subject. It also reminds each “title” of the parent section each time a sub-section is said: the user does not have to keep in memory/mind all the hierarchy to situate the data that is expressed. Using the recursive numbering code, implementing such a functionality is very simple. It is only another way outputting the elements. •
RSS Feeds
RSS stands for Really Simple Syndication (or at least it is suppose to be... people are fighting and new deaths are count every day in this war for truth!). It is anyway a very good way to keep track of the modification of a website and it is an XML language (meaning that all modules that are built to parse XML files can parse RSS feeds). The primer advantage of RSS feeds are that they give direct access to the information, and only the valuable information. This is a MIRACLE for us, we will only give valuable information to the user, he/she will not have to go through - 77 out of 78 -
some un-understandable menus in JavaScipt, DHTML, or even worse Flash and be able to just be given what he/she wants. RSS is also organised is such a way that information are structured and be classed in different ways. RSS is based on HTML (XHTML to be precise) to provide formatted content and therefore the only big part of such an implementation is to write a RSS document parser. The RSS 2.0 Specification is available at harvard.edu and a validator is available at feedvalidator.org. •
Favourites as web searchers
We can imagine a way for the user to configure some of his/her favourite as a web searcher. Basically a web searcher is a form (with it action attribute) that contains a text field. The only information we have to know are the value of the action attribute of the form and the value of the name attribute of the text field.
The information that should be kept from the form in order to emulate a web search.
Then we just have to recognise a special rule like the one we are using for text fields in forms and emulate a form sending. •
Configuring favourites for the “good information”
This is maybe the most complex improvement to implement but at the same time the most valuable for the user. I will simply describe the basic idea: only provide the user the information he/she wants. To do so, the user has to be able to set very easily some rules that the system will apply before processing a favourite document. • • • • •
Delete everything after a fixed point. Only keep the information that is using a particular style. Keep only after a fixed point. Keep only before a fixed point Stop after n words.
• • • • •
Delete everything before a fixed point. Delete information that is using a particular style. Delete all images. Delete all forms. ...
Some example of rules that could be used and combined together
- 78 out of 78 -
VoiceXML Web Browser Appendix 2003/2004 Pierre Naquin
Document Name Author Name Author Student Number Author Course Document Status Submission Date
VoiceXML Web Browser Appendix Pierre Naquin 03120748 MSc in Computer Science Final 10/09/2004
Code — 1 out of 95 —
htdocs alphabet.grxml htdocs › alphabet.grxml
a b c d e f g h i j k l m n o p q r s t u v w x y z 1 2 3 4 5 6 7 8 9 0
boolean.grxml htdocs › boolean.grxml
yes no
login.vxml htdocs › login.vxml
Welcome to the VoiceXML Web Brother. Please enter your login. Please enter your password. Would you like us to send your password by email?
— 3 out of 95 —
cgi-bin close.cgi cgi-bin › close.cgi
#!/perl/bin/perl #close.cgi - Close an opened windows. use CGI ; use XML::Simple ; use HTTP::Lite ; my $CGIHandle = new CGI ; $url = $CGIHandle->param("url") ; $selfUrl = $CGIHandle->param("selfUrl") ; $user = $CGIHandle->param("user") ; $password = $CGIHandle->param("password") ; $windowNb = $CGIHandle->param("windowNb") ; $windowToCloseNb = $CGIHandle->param("windowToCloseNb") ; $type = $CGIHandle->param("type") ; if (checkLogin()) { if ($type eq "single") { deleteFiles() ; } elsif ($type eq "all") { deleteAllFiles() ; } redirectToGo() ; } sub checkLogin { my $config = XMLin("./general.xml") ; my $userFileURL = $config->{usersFile}->{src} ; my $result = 0 ; $config = XMLin('./' . $userFileURL) ; my @tmp = $config->{user} ; for (my $i = 0; $i {user}->[$i]->{login}) && ($password eq $config->{user}->[$i]->{password})) { $result = 1 ; $userID = $i ; } } if (!$result) { buildVXMLError("A problem occurred with your login or password.") ; } return $result ; } sub buildVXMLError {
— 4 out of 95 —
my $msg = $_[0] ; print $CGIHandle->header(-type => "application/voicexml+xml") ; print '' . "\n" . '' . "\n" . ' ' . "\n" . ' ' . $msg . '' . "\n" . ' ' . "\n" . '' ; exit ; } sub redirectToGo { redirectWithPost(getDirectory() . '/go.cgi', {'url' => $url, 'selfUrl' => $selfUrl, 'user' => $user, 'password' => $password, 'windowNb' => $windowNb}) ; } sub getDirectory { my $CGIHandle = new CGI ; my ($directory) = $CGIHandle->self_url() =~ m!^(.+)/.*$!i ; return $directory ; } sub redirectWithPost { my $redirectionUrl = $_[0] ; my $rpostData = $_[1] ; $HTTPHandle = new HTTP::Lite ; $HTTPHandle->prepare_post($rpostData) ; $HTTPHandle->request($redirectionUrl) or buildVXMLError("Unable to access the requested web page: " . $redirectionUrl . '.') ; print $HTTPHandle->headers_string() ; print $HTTPHandle->body() ; } sub deleteFiles { unlink('./users/' . $user . '/temp/' . $windowToCloseNb . '.grxml' ; unlink('./users/' . $user . '/temp/' . $windowToCloseNb . '.vxml' ; if (($windowToCloseNb == $windowNb) && ($windowNb > 0)) { $windowNb -= 1 ; } } sub deleteAllFiles { # FOLLOWING URLS MUST CHANGE WHEN SYSTEM MOVES redirectWithPost('http://localhost/cgi-bin/DISSERTATION/logoff.cgi', {'user' => $user, 'password' => $password}) ; redirectWithPost('http://localhost/cgi-bin/DISSERTATION/login.cgi', {'login' => $user, 'password' => $password}) ;
— 5 out of 95 —
} sub deleteWindowFromIndex { open(IN, './users/' . $user . '/temp/index.list') ; print OUT @tmpList ; close(OUT) ; } sub deleteAllIndexes { unlink('./users/' . $user . '/temp/index.list' ; }
favorite.cgi cgi-bin › favorite.cgi
#!/perl/bin/perl #favorite.cgi - Add a window URL to the favourite list. use CGI ; use XML::Simple ; use HTTP::Lite ; my $CGIHandle = new CGI ; $url = $CGIHandle->param("url") ; $selfUrl = $CGIHandle->param("selfUrl") ; $user = $CGIHandle->param("user") ; $password = $CGIHandle->param("password") ; $windowNb = $CGIHandle->param("windowNb") ; $windowToAddNb = $CGIHandle->param("windowToAddNb") ; $favoriteName = $CGIHandle->param("favoriteName") ; if (checkLogin()) { addToFavorite() ; redirectToGo() ; } sub checkLogin { my $config = XMLin("./general.xml") ; my $userFileURL = $config->{usersFile}->{src} ; my $result = 0 ; $config = XMLin('./' . $userFileURL) ; my @tmp = $config->{user} ; for (my $i = 0; $i {user}->[$i]->{login}) && ($password eq $config->{user}->[$i]->{password})) { $result = 1 ; $userID = $i ; } }
— 6 out of 95 —
if (!$result) { buildVXMLError("A problem occured with your login or password.") ; } return $result ; } sub addToFavorites { open(IN, '>./users/' . $user . '/favorite.list') ; print FAV $favoriteName . "\t" . $tmpList[$windowToAddNb] ; close(FAV) ; } sub redirectToGo { redirectWithPost(getDirectory() . '/go.cgi', {'url' => $url, 'selfUrl' => $selfUrl, 'user' => $user, 'password' => $password, 'windowNb' => $windowNb}) ; } sub getDirectory { my $CGIHandle = new CGI ; my ($directory) = $CGIHandle->self_url() =~ m!^(.+)/.*$!i ; return $directory ; } sub buildVXMLError { my $msg = $_[0] ; print $CGIHandle->header(-type => "application/voicexml+xml") ; print '' . "\n" . '' . "\n" . ' ' . "\n" . ' ' . $msg . '' . "\n" . ' ' . "\n" . '' ; exit ; } sub redirectWithPost { my $redirectionUrl = $_[0] ; my $rpostData = $_[1] ; $HTTPHandle = new HTTP::Lite ; $HTTPHandle->prepare_post($rpostData) ; $HTTPHandle->request($redirectionUrl) or buildVXMLError("Unable to access the requested web page: " . $redirectionUrl . '.') ;
— 7 out of 95 —
print $HTTPHandle->headers_string() ; print $HTTPHandle->body() ; }
general.xml cgi-bin › general.xml
go.cgi cgi-bin › go.cgi
#!/perl/bin/perl #go.cgi - Generate an HTTP request and lauch the processing. use use use use use
CGI ; XML::Simple ; HTTP::Lite ; Archive::Zip ; threads ;
my $CGIHandle = new CGI ; $url = $CGIHandle->param("url") ; $selfUrl = $CGIHandle->param("selfUrl") ; $user = $CGIHandle->param("user") ; $password = $CGIHandle->param("password") ; $windowNb = $CGIHandle->param("windowNb") ; @openedTagStack ; %globalTagVariables ; $out1 ; $out2 ; performRequest() ; sub buildRequest { my @allParameters = $CGIHandle->param() ; my @realParameters ; my $empty = 1 ; my $parameter ; foreach $parameter (@allParameters) { if (($parameter ne "url") & ($parameter ne "selfUrl") && ($parameter ne "user") && ($parameter ne "password") && ($parameter ne "windowNb")) { push(@realParameters, $parameter) ; $empty = 0 ; } } if ($empty) { return urlBuilding($url, $selfUrl) ; } else { my %tmpVars ;
— 8 out of 95 —
foreach $parameter (@realParameters) { $tmpVars{$parameter} = $realParameters[$parameter] ; } return (urlBuilding($url, $selfUrl), \%tmpVars) ; } } sub performRequest { if (checkLogin() && $url && $selfUrl) { my $redirectionUrl ; my $HTTPHandle ; my $rightUrl ; my $rpostData ; do { $HTTPHandle = new HTTP::Lite ; $HTTPHandle->http11_mode(1) ; ($rightUrl, $rpostData) = buildRequest() ; if ($rpostData) { $HTTPHandle->prepare_post($rpostData) ; } $request = $HTTPHandle->request($rightUrl) or buildVXMLError("Unable to access the requested web page: " . $rightUrl . '.') ; $redirectionUrl = checkHeaderForRedirections( $HTTPHandle->headers_array()) ; $selfUrl = urlBuilding($url, $selfUrl) ; if ($redirectionUrl) { $url = urlBuilding($redirectionUrl, $url) ; } } while ($redirectionUrl) ; downloadCompressAndSave($HTTPHandle->body()) ; #my $zipThread = threads->create("downloadCompressAndSave", $HTTPHandle->body()) ; startProcessing($HTTPHandle->body()) ; #$zipThread->join() ; } } sub getHostName { my $tmpUrl = $_[0] ; $tmpUrl =~ m!(http://(?:[^/:]+(?::(?:\d+))?))(?:/.*)?!i ; return $1 . '/' ; } sub getHostDirectory { my $tmpUrl = $_[0] ; $tmpUrl =~ m!(http://(?:[^/:]+(?::(?:\d+))?).*)(?:/[^/]*)!i ; return ((substr($1, -1, 1) eq "/") ? $1 : $1 . '/') ; }
— 9 out of 95 —
sub getHostPreviousDirectory { my $tmpUrl = $_[0] ; $tmpUrl =~ m!(http://(?:[^/:]+(?::(?:\d+))?).*/)(?:[^/]+/[^/]*)!i ; return ((substr($1, -1, 1) eq "/") ? $1 : $1 . '/') ; } sub checkHeaderForRedirection { my @headers = @_ ; my $header ; foreach $header (@headers) { if ($header =~ m/Location:\s+(\S+)/i) { return $1 ; } } return 0 ; } sub urlBuilding { my $tmpUrl = $_[0] ; my $tmpPreviousUrl = $_[1] ; if (substr($tmpUrl, 0, 2) eq "..") { return getHostPreviousDirectory($tmpPreviousUrl) . substr($tmpUrl, 3) ; } elsif (substr($tmpUrl, 0, 2) eq "./") { return getHostDirectory($tmpPreviousUrl) . substr($tmpUrl, 2) ; } elsif (substr($tmpUrl, 0, 1) eq "/") { return getHostName($tmpPreviousUrl) . substr($tmpUrl, 1) ; } elsif (substr($tmpUrl, 0, 7) eq "http://") { return $tmpUrl ; } else { return getHostDirectory($tmpPreviousUrl) . $tmpUrl ; } } sub replaceText { my ($oldText, $start, $end, $newText) = @_ ; return substr($oldText, 0, $start) . $newText . substr($oldText, $end) ; } sub extractExtension { my $inputText = $_[0] ; $inputText =~ m!\w+\.(\w+)!i ; return $1 ; } sub buildTagLists { my $config = XMLin("./general.xml") ; my $tagFileURL = $config->{rulesFile}->{src} ; $config = XMLin('./' . $tagFileURL) ; my $rtmpTagLists = $config->{tag} ;
— 10 out of 95 —
return $rtmpTagLists ; } sub searchAndProcessLoop { my ($text, $rtmpTagGeneralLists) = @_ ; my $lastTagPos = 0 ; my $workingText = $text ; while (($lastTagPos = selectAction($workingText, $rtmpTagGeneralLists)) ne "FINISHED") { $workingText = substr($workingText, $lastTagPos) ; } } sub findTag { my $text = $_[0] ; if ($text =~ m/(])*>)/) { return ($1, $-[0], $+[0]) ; } else { return 0 ; } } sub extractTagName { my $tag = $_[0] ; $tag =~ m!)!i ; return $1 ; } sub isOpeningTag { my $tag = $_[0] ; $tag =~ !{tag}->{$tagName} ; if (isOpeningTag($tag) && (($rtmpTagGeneralLists-> {$tmpTag}->{type} eq 'non-closed') || ($rtmpTagGeneralLists->{$tmpTag}->{run} eq 'before'))) { $funcCall = 'require("./' . $rtmpTagGeneralLists>{$tmpTag}->{call} . '") ; run_' . $tmpTag . '($rtagPref, $tag) ;' ; } elsif (isOpeningTag($tag) && ($rtmpTagGeneralLists>{$tmpTag}->{run} eq 'before-after')) { $funcCall = 'require("./' . $rtmpTagGeneralLists>{$tmpTag}->{call} . '") ; run_' . $tmpTag . '_before($rtagPref, $tag) ;' ; } for ($i = (scalar @openedTagStack) - 1, $flag = 1; $i >= 0 && $flag; $i -= 1) { if (isClosingTag($tag) && $openedTagStack[$i] eq $tmpTag) { if ($rtmpTagGeneralLists->{$openedTagStack [$i]}->{run} eq 'before-after') { $funcCall = 'require("./' . $rtmpTagGeneralLists->{$openedTagStack[$i]}->{call} . '") ; run_' . $openedTagStack[$i] . '_after($rtagPref, $tag) ;' ; $flag = 0 ; } splice(@openedTagStack, $i, 1) ; $flag = 0 ; } elsif (isOpeningTag($tag) && (testArrayForValue($rtmpTagGeneralLists-> {$openedTagStack[$i]}->{blocking}, $tmpTag) || $rtmpTagGeneralLists->{$openedTagStack[$i]}-> {blocking} eq $tmpTag || $rtmpTagGeneralLists->{$openedTagStack[$i]}-> {blocking} eq 'all')) { if ($rtmpTagGeneralLists-> {$openedTagStack[$i]}->{run} eq 'before-after') { $funcCall = 'require("./' . $rtmpTagGeneralLists->{$openedTagStack[$i]}->{call} . '") ; run_' . $openedTagStack[$i] . '_after($rtagPref, $tag) ;' ; my ($returnValue1, $returnValue2) = eval($funcCall) ; if ($returnValue1 ne '1') { $out1 .= $returnValue1 ; $out2 .= $returnValue2 ; } if (($rtmpTagGeneralLists->{$tmpTag}-> {type} eq 'non-closed') || ($rtmpTagGeneralLists->{$tmpTag}->{run}
— 12 out of 95 —
eq 'before')) { $funcCall = 'require("./' . $rtmpTagGeneralLists->{$tmpTag}->{call} . '") ; run_' . $tmpTag . '($rtagPref, $tag) ;' ; } elsif ($rtmpTagGeneralLists-> {$tmpTag}->{run} eq 'before-after') { $funcCall = 'require("./' . $rtmpTagGeneralLists->{$tmpTag}->{call} . '") ; run_' . $tmpTag . '_before($rtagPref, $tag) ;' ; } } splice(@openedTagStack, $i, 1) ; $flag = 0 ; } } if (isOpeningTag($tag) && ($rtmpTagGeneralLists-> {$tmpTag}->{type} eq 'closed')) { push(@openedTagStack, $tmpTag) ; } } } my ($returnValue1, $returnValue2) = eval($funcCall) ; if ($returnValue1 ne '1') { $out1 .= $returnValue1 ; $out2 .= $returnValue2 ; } return $tagClosePos ; } sub startProcessing { my $bodyText = $_[0] ; my $rtmpTagGeneralLists = buildTagLists() ; addWindowToIndex() ; startOut() ; favoriteOut() ; searchAndProcessLoop($bodyText, $rtmpTagGeneralLists) ; endTagProcessing($rtmpTagGeneralLists) ; switchToWindowOut() ; listFavoriteOut() ; openFavoriteOut() ; addToFavoriteOut() ; openURLOut() ; emailOut() ; closeWindowOut() ; finishOut() ; saveOutToFile() ; # FOLLOWING URL MUST CHANGE WHEN SYSTEM MOVES print $CGIHandle->redirect(-url => 'http://localhost/cgibin/DISSERTATION/users/' . $user . '/temp/' . $windowNb . '.vxml') ; } sub endTagProcessing { $rtmpTagList = $_[0] ; for (my $i = ((scalar (keys %$rtmpTagList)) - 1); $i >= 0; $i -= 1) { $tmp = (keys %$rtmpTagList)[$i] ; if ($rtmpTagList->{$tmp}->{end} eq "true") { my ($returnValue1, $returnValue2) = eval('require("./' . $rtmpTagList->{$tmp}->{call} . '") ; run_' . $tmp . '_end()') ;
— 13 out of 95 —
if ($returnValue1 ne '1') { $out1 .= $returnValue1 ; $out2 .= $returnValue2 ; } } } } sub downloadCompressAndSave { my $allBody = $_[0] ; my $tmpUrl ; my $tag ; my $insideTag ; my @allFiles ; my @allFilesExtensions ; my $HTTPHandle ; my $i ; my @start ; my @end ; while ($allBody =~ m!]+)>!ig) { $tag = $1 ; $insideTag = $2 ; @start = @- ; @end = @+ ; if (((($tag eq "applet") || ($tag eq "frame") || ($tag eq "img") || ($tag eq "script")) && ($insideTag =~ m!\bsrc\s*=\s*(?:"([^"]*)"|'([^']*)'|([^'">\s]+))!xi)) || (($tag eq "link") && ($insideTag =~ m!\bhref\s*=\s*(?:"([^"]*)"|'([^']*)'|([^'">\s]+))!xi)) || (($tag eq "object") && ($insideTag =~ m!\bdata\s*=\s*(?:"([^"]*)"|'([^']*)'|([^'">\s]+))!xi)) || (($tag eq "body") && ($insideTag =~ m!\bbackground\s*=\s*(?:"([^"]*)"|'([^']*)'|([^'">\s]+))!xi))) { $tmpUrl = $+ ; foreach ($i = 1; $i < (scalar @-); $i += 1) { if ($-[$i]) { $insideTag = replaceText($insideTag, $-[$i], $+[$i], (scalar @allFiles) . '.' . extractExtension($tmpUrl)) ; } } $HTTPHandle = new HTTP::Lite ; $HTTPHandle->request(urlBuilding($tmpUrl, $selfUrl)) or buildVXMLError("Unable to access the requested web page: " . $rightUrl . '.') ; $allFiles[(scalar @allFiles)] = $HTTPHandle->body() ; $allFilesExtensions[(scalar @allFilesExtensions)] = extractExtension($tmpUrl) ; } $allBody = replaceText($allBody, $start[2], $end[2], $insideTag) ; pos($allBody) = $end[2] ; } my $zipFile = Archive::Zip->new() ; $zipFile->addString($allBody, 'index.html') ; for ($i = 0; $i < (scalar @allFiles); $i += 1) { $zipFile->addString($allFiles[$i], $i . '.' . $allFilesExtensions[$i]) ; } if ($zipFile->writeToFileNamed('./users/' . $user . '/temp/' . $windowNb . '.zip') != AZ_OK) { buildVXMLError("Error writing temporary file.") ; } }
— 14 out of 95 —
sub nextWindowAvailable { open(IN, './users/' . $user . '/temp/index.list') ; print OUT @tmpList ; close(OUT) ; } sub startOut { $out1 .= '' . "\n" . '' . "\n" . ' ' . "\n" ; $out1 .= "\t\t" . "\n" ; $out1 .= "\t\t" $out1 .= "\t\t" . "\n" ; $out1 .= "\t\t" . "\n" ;
. '' . '' . "\n" ; . '' . ''
$out1 .= "\t\t" . '' . "\n" ; $out2 = '' . "\n" . '' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . "\n" . . ' ' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . "\n" .
— 15 out of 95 —
' . ' . ' . ' . ' . ' . ' . ' '
' "\n" . ' "\n" . ' "\n" . ' "\n" . ' "\n" . ' "\n" . ' "\n" . ' . "\n" . ' . "\n" ;
} sub favoriteOut { my $config = XMLin("./general.xml") ; my $userFileURL = $config->{usersFile}->{src} ; $config = XMLin('./' . $userFileURL) ; my $userFavoriteURL = $config->{user}->[$userID]-> {favoriteFile}->{src} ; open(IN, ' ' . "\n" ; $out2 .= "\t\t" . '' . "\n" ; $out2 .= "\t\t\t" . '' . "\n" ; for (my $i = 0; $i < (scalar @favoriteList); $i += 1) { my ($tmpName, $tmpUrl) = $favoriteList[$i] =~ m!^(.+)\t(.+)$!i ; $out2 .= "\t\t\t\t" . '' . $tmpName . '' . "\n" ; } $out2 .= "\t\t\t" . '' . "\n" ; $out2 .= "\t\t" . '' . "\n" ;
— 16 out of 95 —
} sub addToFavoriteOut { open(IN, './users/' . $user . '/temp/' . $windowNb . '.vxml') ; open(OUT2, '>./users/' . $user . '/temp/' . $windowNb . '.grxml') ; print OUT1 $out1 ; print OUT2 $out2 ; close(OUT1) ; close(OUT2) ; } sub buildVXMLError { $msg = $_[0] ; print $CGIHandle->header(-type => "application/voicexml+xml") ; print '' . "\n" . '' . "\n" . ' ' . "\n" . ' ' . $msg . '' . "\n" . ' ' . "\n" . '' ; exit ; }
— 22 out of 95 —
sub checkLogin { my $config = XMLin("./general.xml") ; my $userFileURL = $config->{usersFile}->{src} ; my $result = 0 ; $config = XMLin('./' . $userFileURL) ; my @tmp = $config->{user} ; for (my $i = 0; $i {user}->[$i]->{login}) && ($password eq $config->{user}->[$i]->{password})) { $result = 1 ; $userID = $i ; } } if (!$result) { buildVXMLError("A problem occured with your login or password.") ; } return $result ; }
login.cgi cgi-bin › login.cgi
#!/perl/bin/perl #login.cgi - Login in the system. use use use use
CGI ; XML::Simple ; HTTP::Lite ; Net::SMTP ;
my $CGIHandle = new CGI ; $login = $CGIHandle->param("login") ; $password = $CGIHandle->param("password") ; $send_pass = $CGIHandle->param("send_pass") ; if ($send_pass eq 'true') { sendPassword() ; } elsif (checkLogin()) { buildBasicGrammars() ; buildDictionaryGrammar() ; my $config = XMLin("./general.xml") ; my $userFileURL = $config->{usersFile}->{src} ; my $result = 0 ; $config = XMLin('./' . $userFileURL) ; $url = $config->{user}->[$userID]->{startUrl}->{src} ; # FOLLOWING URL MUST CHANGE WHEN SYSTEM MOVES redirectWithPost('http://localhost/cgi-bin/DISSERTATION/go.cgi', {'user' => $login, 'password' => $password, 'windowNb' => '0', 'url' => $url, 'selfUrl' => $url}) ;
— 23 out of 95 —
} else { # FOLLOWING URL MUST CHANGE WHEN SYSTEM MOVES redirectWithoutPost('http://localhost/DISSERTATION/login.vxml') ; } sub checkLogin { my $config = XMLin("./general.xml") ; my $userFileURL = $config->{usersFile}->{src} ; my $result = 0 ; $config = XMLin('./' . $userFileURL) ; my @tmp = $config->{user} ; for (my $i = 0; $i {user}->[$i]->{login}) && ($password eq $config->{user}->[$i]->{password})) { $result = 1 ; $userID = $i ; } } if (!$result) { buildVXMLError("A problem occurred with your login or password.") ; } return $result ; } sub buildVXMLError { my $msg = $_[0] ; print $CGIHandle->header(-type => "application/voicexml+xml") ; print '' . "\n" . '' . "\n" . ' ' . "\n" . ' ' . $msg . '' . "\n" . ' ' . "\n" . '' ; exit ; } sub redirectWithoutPost { my $redirectionUrl = $_[0] ; $HTTPHandle = new HTTP::Lite ; $HTTPHandle->request($redirectionUrl) or buildVXMLError("Unable to access the requested web page: " . $redirectionUrl . '.') ; print $HTTPHandle->headers_string() ; print $HTTPHandle->body() ; }
— 24 out of 95 —
sub redirectWithPost { my $redirectionUrl = $_[0] ; my $rpostData = $_[1] ; $HTTPHandle = new HTTP::Lite ; $HTTPHandle->prepare_post($rpostData) ; $HTTPHandle->request($redirectionUrl) or buildVXMLError("Unable to access the requested web page: " . $redirectionUrl . '.') ; print $HTTPHandle->headers_string() ; print $HTTPHandle->body() ; } sub sendPassword { my $config = XMLin("./general.xml") ; my $userFileURL = $config->{usersFile}->{src} ; my $sendPassword = 0 ; $config = XMLin('./' . $userFileURL) ; my @tmp = $config->{user} ; for (my $i = 0; $i {user}->[$i]->{login}) { $sendPassword = $config->{user}->[$i]->{password} ; } } if ($sendPassword) { my ($emailSender, $emailFullSender, $SMTPServer, $emailSubject) = findEmailConf() ; my ($userEmail, $userFullName) = findUserInfo($user) ; $SMTPMsg = Net::SMTP->new($SMTPServer) or buildVXMLError("Error connecting to mailserver.") ; $SMTPMsg->mail(emailSender) ; $SMTPMsg->to($userEmail) ; $SMTPMsg->data() ; $SMTPMsg->datasend('To: ' . $userFullName . ' ' . "\n") ; $SMTPMsg->datasend('From: ' . $emailFullSender . "\n") ; $SMTPMsg->datasend('Subject: Lost Password.' . "\n\n") ; $SMTPMsg->datasend('Login: '. $login . "\nPassword: " . $sendPassword . "\n\n") ; $SMTPMsg->dataend() ; $SMTPMsg->quit() ; } else { buildVXMLError('The login ' . $login . 'does not exist.') ; } } sub findEmailConf { my $config = XMLin("./general.xml") ; my $rulesFileURL = $config->{rulesFile}->{src} ;
— 25 out of 95 —
$config = XMLin('./' . $rulesFileURL) ; return ($config->{emailSender}->{address}, $config->{emailSender}-> {full}, $config->{SMTPServer}->{address}, $config-> {emailSubject}->{subject}) ; } sub findUserInfo { my $workingUser = $_[0] ; my $config = XMLin("./general.xml") ; my $userFileURL = $config->{usersFile}->{src} ; $config = XMLin('./' . $userFileURL) ; @tmp = $config->{user} ; for (my $i = 0; $i {user}->[$i]->{login}) { return ($config->{user}->[$i]->{email}, $config-> {user}->[$i]->{fullName}) ; } } } sub buildDictionaryGrammar { my $out = '' . "\n" . '' . "\n" . ' ' . "\n" . ' ' . "\n" ; my $config = XMLin("./general.xml") ; my $userFileURL = $config->{usersFile}->{src} ; $config = XMLin('./' . $userFileURL) ; $selectedDictionary = $config->{user}->[$userID]->{dictionary}-> {global}->{tag} ; open(IN, "
des documents recommandant