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
|
with CGI, Text_IO, Ada.Integer_Text_IO, Ada.Strings.Unbounded,
Ada.Characters.Handling, Ada.Strings.Maps.Constants, Ustrings;
use CGI, Text_IO, Ada.Integer_Text_IO, Ada.Strings.Unbounded,
Ada.Characters.Handling, Ada.Strings.Maps.Constants, Ustrings;
procedure Search is
-- Search for a requested search string in a requested text file;
-- the request and reply use the Common Gateway Interface (CGI) to
-- an HTTP server, and then on to a user of a World Wide Web (WWW) browser.
-- It's basically a web application version of "grep", with security features
-- that let the server select which files a user can search.
-- If a search string _and_ file name is sent, a search result is returned.
-- Otherwise, the program will reply with a form to fill out.
-- If some information (such as the file to search) is provided,
-- the form is specialized returned to "remember" the previous values sent.
-- This program can search many different files, but each file _MUST_ be
-- listed in the file "srchlist". The format for the "srchlist" file
-- is a list of lines with the following format:
-- local_file_name,User_Name
-- Here's an example of an entry in "srchlist":
-- /public/addresses/phone,Phone List
-- Srchlist lines beginning with "#" are comment lines and are ignored.
-- To run this program directly (without an HTTP server), set the
-- environment variable REQUEST_METHOD to "GET" and the variable
-- QUERY_STRING to the query values, such as "" or
-- "file=Phone%20List&query=David&casesensitive=no".
-- Returning specialized forms takes a little extra effort since
-- package CGI doesn't automatically do this; a higher-level interface
-- than package CGI could simplify handling partially completed forms.
-- Copyright (C) 1995-2000 David A. Wheeler
--
-- This program is free software; you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation; either version 2 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program; if not, write to the Free Software
-- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-- CHANGE THIS LINE TO WHEREVER YOUR SEARCH LIST FILE IS:
Search_List_Filename : constant String := "/home/httpd/srchlist";
Search_List : File_Type;
function User_Name(S : Unbounded_String) return Unbounded_String is
X : Natural;
begin
X := Index(S, ",");
return To_Unbounded_String(Slice(S, X+1, Length(S)));
end User_Name;
function Real_File_Name(S : Unbounded_String) return String is
Line : Unbounded_String;
begin
while not End_Of_File(Search_List) loop
Ustrings.Get_Line(Search_List, Line);
if Element(Line, 1) /= '#' then
if User_Name(Line) = S then
return Slice(Line, 1, Index(Line, ",") - 1);
end if;
end if;
end loop;
return "";
end Real_File_Name;
procedure Put_Matches(Filename : String; Pattern : String;
Case_Sensitive : Boolean) is
Found_Something : Boolean := False;
Found_Here : Boolean := False;
Line : Unbounded_String;
Match_To : Unbounded_String;
Search_File : File_Type;
Clean_Pattern : String(1 .. Pattern'Length);
-- Given Filename, pattern, and case sensitivity, put matches.
-- This is currently implemented in a slow, inefficient way, but it's
-- sufficient for the purpose for non-monstrous files.
begin
if not Case_Sensitive then
Clean_Pattern := To_Lower(Pattern);
else
Clean_Pattern := Pattern;
end if;
if Filename = "" then
Put_Line("<p><i>Sorry, that's not a searchable file.</i><p>");
return;
end if;
if Pattern = "" then
Put_Line("<p><i>Sorry, empty patterns are not permitted.</i><p>");
return;
end if;
Open(Search_File, In_File, Filename);
while not End_Of_File(Search_File) loop
Get_Line(Search_File, Line);
if not Case_Sensitive then
Match_To := Translate(Line, Lower_Case_Map);
Found_Here := (Index(Match_To, Clean_Pattern) /= 0);
else
Found_Here := (Index(Line, Clean_Pattern) /= 0);
end if;
if Found_Here then
Put_Line(HTML_Encode(Line));
Found_Something := True;
end if;
end loop;
if not Found_Something then
Put_Line("<i>No matches found</i>");
end if;
exception
when Name_Error =>
Put_Line("<i>File to search is not available</i>");
end Put_Matches;
procedure Process_Query is
User_File_To_Search : constant String := CGI.Value("file");
-- Note that users can't pick the filename; instead, the user provides
-- the "file" value, and the program controls the conversion to a filename.
-- That way, users can't view arbitrary files.
File_To_Search : constant String := Real_File_Name(U(User_File_To_Search));
Pattern : constant String := Value("query");
Case_Sensitive : Boolean := False;
Case_Sensitivity : constant String := Value ("casesensitive");
begin
Put_HTML_Head("Query Result");
Put_HTML_Heading("Query Result", 1);
Put_Line(String'("<p>The search for <i>" & HTML_Encode(Value("query"))));
Put_Line(String'("</i> in file <i>" & HTML_Encode(Value("file")) & "</i>"));
if Case_Sensitivity = "yes" then
Case_Sensitive := True;
Put_Line(" in a case-sensitive manner");
end if;
Put_Line("produced the following result:<p>");
Put_Line("<pre>");
Flush;
Put_Matches(File_To_Search, Pattern, Case_Sensitive);
Put_Line("</pre>");
end Process_Query;
procedure Put_Select_List is
Line : Unbounded_String;
First_Option : Boolean := True;
Number_Of_Options : Natural := 0;
begin
-- Put a Selection list of legal filenames out.
-- Count the number of options (non-comment lines).
while not End_Of_File(Search_List) loop
Get_Line(Search_List, Line);
if Element(Line, 1) /= '#' then
Number_Of_Options := Number_Of_Options + 1;
end if;
end loop;
Reset(Search_List);
Put("<select name=""file"" size=""");
Put(Number_Of_Options, Width => 0);
Put_Line(""">");
while not End_Of_File(Search_List) loop
Get_Line(Search_List, Line);
if Element(Line, 1) /= '#' then
Put("<option value=""");
Put(HTML_Encode(User_Name(Line)));
Put('"');
if First_Option then
Put(" selected");
First_Option := False;
end if;
Put('>');
Put_Line(HTML_Encode(User_Name(Line)));
end if;
end loop;
Put_Line("</select> <p>");
end Put_Select_List;
function Open_Search_List return Boolean is
begin
Open(Search_List, In_File, Search_List_Filename);
return True;
exception
when Name_Error =>
Put_Error_Message("Search List File is not available");
return False;
when Others =>
Put_Error_Message("Search List File could not be opened");
return False;
end Open_Search_List;
procedure Generate_Blank_Form is
Query_String : constant String := CGI.Value ("query");
File_Value : constant String := CGI.Value ("file");
begin
Put_HTML_Head("Text Search Form");
Put_HTML_Heading("Text Search Form", 1);
Put_Line("<p>You may search for a text phrase");
Put_Line(" from any of the given files.<p>");
Put_Line("<form method=""POST"">");
Put_Line("What do you want to search for:<p>");
Put("<input name=""query"" size=""40""");
-- if query was set, use it as the default.
if Query_String /= "" then
Put(" value=""");
-- We encoding this so queries with '<', '"', etc work correctly.
Put(String'(HTML_Encode(Value("query"))));
Put('"');
end if;
Put_Line("><p>");
-- If file was set, save it in the form and note it as a fixed value.
-- Otherwise, let the user pick the file to search.
if Key_Exists("file") and File_Value /= "" then
Put("<input type=""hidden"" name=""file"" value=""");
Put(String'(HTML_Encode(Value("file")) & """>"));
Put("<p>You will be searching file <i>");
Put(String'(HTML_Encode(Value("file"))));
Put_Line("</i><p>");
else
Put_Line("Where do you want to search?<p>");
Put_Select_List;
end if;
-- If "casesensitive" set, save it in the form (invisibly).
-- Otherwise, let the user choose.
if Key_Exists("casesensitive") then
Put("<input type=""hidden"" name=""casesensitive"" value=""");
Put(String'(HTML_Encode(Value("casesensitive"))));
Put_Line(""">");
else
Put_Line("Do you want this search to be case-sensitive?");
Put_Line("<dl>");
Put_Line("<dd><input type=""radio"" name=""casesensitive"" " &
"value=""yes""> <i>Yes.</i>");
Put_Line("<dd><input type=""radio"" name=""casesensitive"" " &
"value=""no"" checked> <i>No.</i>");
Put_Line("</dl>");
end if;
Put_Line("<p> <input type=""submit"" value=""Submit Query"">");
Put_Line("<input type=""reset""> ");
Put_Line("</form>");
end Generate_Blank_Form;
begin
Put_CGI_Header;
if not Open_Search_List then
return; -- Can't open search list, don't go any further.
end if;
if Key_Exists("query") and Key_Exists("file") then
Process_Query;
else
Generate_Blank_Form;
end if;
Put_HTML_Tail;
Close(Search_List);
end Search;
|