ezcgi.pp 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  1. {
  2. This file is part of the Free Component Library (FCL)
  3. Copyright (c) 1999-2000 by Michael Van Canneyt and Florian Klaempfl
  4. See the file COPYING.FPC, included in this distribution,
  5. for details about the copyright.
  6. This program is distributed in the hope that it will be useful,
  7. but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  9. **********************************************************************}
  10. unit ezcgi;
  11. {$mode delphi}
  12. {$H+ }
  13. interface
  14. uses classes, strings, sysutils;
  15. const
  16. hexTable = '0123456789ABCDEF';
  17. type
  18. ECGIException = class(Exception);
  19. TEZcgi = class(TObject)
  20. private
  21. { Private declarations }
  22. FVariables : TStringList;
  23. FName : String;
  24. FEmail : String;
  25. FQueryString : String;
  26. { Token variables }
  27. aString : String;
  28. aSepStr : String;
  29. aPos : Integer;
  30. aLenStr : Integer;
  31. aLenSep : Integer;
  32. procedure InitToken(aStr, aSep : String);
  33. function NextToken(var aToken : String; var aSepChar : Char) : Boolean;
  34. procedure GetQueryItems;
  35. procedure ProcessRequest;
  36. procedure LoadEnvVariables;
  37. function GetVal(Index : String) : String;
  38. function GetName(Index : Integer) : String;
  39. function GetVariable(Index : Integer) : String;
  40. function GetVarCount : Integer;
  41. procedure ReadPostQuery;
  42. procedure ReadGetQuery;
  43. protected
  44. { Protected declarations }
  45. procedure OutputError(errorMessage : String);
  46. public
  47. { Public declarations }
  48. constructor Create;
  49. destructor Destroy; override;
  50. procedure Run;
  51. procedure WriteContent(ctype : String);
  52. procedure PutLine(sOut : String);
  53. function GetValue(Index : String; defaultValue : String) : String;
  54. procedure DoPost; virtual;
  55. procedure DoGet; virtual;
  56. property Values[Index : String] : String read GetVal;
  57. property Names[Index : Integer] : String read GetName;
  58. property Variables[Index : Integer] : String read GetVariable;
  59. property VariableCount : Integer read GetVarCount;
  60. property Name : String read FName write FName;
  61. property Email : String read FEmail write FEmail;
  62. end;
  63. implementation
  64. { *********** Public Methods *************** }
  65. constructor TEZcgi.Create;
  66. begin
  67. FName := 'No name available';
  68. FEmail := 'Email address unavailable';
  69. FVariables := TStringList.Create;
  70. LoadEnvVariables;
  71. end;
  72. destructor TEZcgi.Destroy;
  73. begin
  74. FVariables.Free;
  75. end;
  76. procedure TEZcgi.Run;
  77. begin
  78. ProcessRequest;
  79. end;
  80. procedure TEZcgi.DoPost;
  81. begin
  82. // Must be overriden by child class
  83. end;
  84. procedure TEZcgi.DoGet;
  85. begin
  86. // Must be overriden by child class
  87. end;
  88. procedure TEZcgi.WriteContent(ctype : String);
  89. begin
  90. writeln('Content-Type: ',ctype);
  91. writeln;
  92. end;
  93. procedure TEZcgi.PutLine(sOut : String);
  94. begin
  95. writeln(sOut);
  96. end;
  97. function TEZcgi.GetValue(Index, defaultValue : String) : String;
  98. begin
  99. result := GetVal(Index);
  100. if result = '' then
  101. result := defaultValue;
  102. end;
  103. { *********** Private Methods *************** }
  104. procedure TEZcgi.LoadEnvVariables;
  105. procedure GetEData(variable : String);
  106. var
  107. tempStr : String;
  108. begin
  109. // This is a system dependent call !!
  110. tempStr := GetEnvironmentVariable(variable);
  111. if tempStr <> '' then
  112. FVariables.Add(variable + '=' + tempStr);
  113. end;
  114. begin
  115. { Standard CGI Environment Variables }
  116. GetEData('AUTH_TYPE');
  117. GetEData('CONTENT_LENGTH');
  118. GetEData('CONTENT_TYPE');
  119. GetEData('GATEWAY_INTERFACE');
  120. GetEData('PATH_INFO');
  121. GetEData('PATH_TRANSLATED');
  122. GetEData('QUERY_STRING');
  123. GetEData('REMOTE_ADDR');
  124. GetEData('REMOTE_HOST');
  125. GetEData('REMOTE_IDENT');
  126. GetEData('REMOTE_USER');
  127. GetEData('REQUEST_METHOD');
  128. GetEData('SCRIPT_NAME');
  129. GetEData('SERVER_NAME');
  130. GetEData('SERVER_PORT');
  131. GetEData('SERVER_PROTOCOL');
  132. GetEData('SERVER_SOFTWARE');
  133. { Standard HTTP Environment Variables }
  134. GetEData('HTTP_ACCEPT');
  135. GetEData('HTTP_ACCEPT_CHARSET');
  136. GetEData('HTTP_ACCEPT_ENCODING');
  137. GetEData('HTTP_IF_MODIFIED_SINCE');
  138. GetEData('HTTP_REFERER');
  139. GetEData('HTTP_USER_AGENT');
  140. end;
  141. procedure TEZcgi.ProcessRequest;
  142. var
  143. request : String;
  144. begin
  145. request := GetVal('REQUEST_METHOD');
  146. if request = '' then
  147. OutputError('No REQUEST_METHOD passed from server!')
  148. else if request = 'POST' then
  149. begin
  150. ReadPostQuery;
  151. DoPost;
  152. end
  153. else if request = 'GET' then
  154. begin
  155. ReadGetQuery;
  156. DoGet;
  157. end
  158. else
  159. OutputError('Invalid REQUEST_METHOD passed from server!');
  160. end;
  161. function TEZcgi.GetVal(Index : String) : String;
  162. begin
  163. result := FVariables.Values[Index];
  164. end;
  165. function TEZcgi.GetName(Index : Integer) : String;
  166. begin
  167. result := FVariables.Names[Index];
  168. end;
  169. function TEZcgi.GetVariable(Index : Integer) : String;
  170. begin
  171. result := FVariables[Index];
  172. end;
  173. function TEZcgi.GetVarCount : Integer;
  174. begin
  175. result := FVariables.Count;
  176. end;
  177. procedure TEZcgi.ReadPostQuery;
  178. var
  179. index : Integer;
  180. ch : Char;
  181. temp : String;
  182. code : Word;
  183. contentLength : Integer;
  184. theType : String;
  185. begin
  186. temp := GetVal('CONTENT_LENGTH');
  187. if Length(temp) > 0 then
  188. begin
  189. Val(temp, contentLength, code);
  190. if code <> 0 then
  191. contentLength := 0;
  192. end;
  193. if contentLength = 0 then
  194. OutputError('No content length passed from server!');
  195. theType := UpperCase(GetVal('CONTENT_TYPE'));
  196. if theType <> 'APPLICATION/X-WWW-FORM-URLENCODED' then
  197. OutputError('No content type passed from server!');
  198. FQueryString := '';
  199. for index := 0 to contentLength-1 do
  200. begin
  201. Read(ch);
  202. FQueryString := FQueryString + ch;
  203. end;
  204. GetQueryItems;
  205. end;
  206. procedure TEZcgi.ReadGetQuery;
  207. begin
  208. FQueryString := GetVal('QUERY_STRING');
  209. if FQueryString = '' then
  210. OutputError('No QUERY_STRING passed from server!');
  211. GetQueryItems;
  212. end;
  213. procedure TEZcgi.GetQueryItems;
  214. var
  215. queryItem : String;
  216. delimiter : Char;
  217. function hexConverter(h1, h2 : Char) : Char;
  218. var
  219. thex : byte;
  220. begin
  221. tHex := (Pos(upcase(h1), hexTable) - 1) * 16;
  222. tHex := tHex + Pos(upcase(h2), hexTable) - 1;
  223. result := chr(thex);
  224. end;
  225. procedure Convert_ESC_Chars;
  226. var
  227. index : Integer;
  228. begin
  229. repeat
  230. index := Pos('+', queryItem);
  231. if index > 0 then
  232. queryItem[index] := Chr(32);
  233. until index = 0;
  234. repeat
  235. index := Pos('%', queryItem);
  236. if index > 0 then
  237. begin
  238. queryItem[index] := hexConverter(queryItem[index + 1], queryItem[index + 2]);
  239. system.Delete(queryItem, index + 1, 2);
  240. end;
  241. until index = 0;
  242. end;
  243. begin
  244. InitToken(FQueryString, '&');
  245. while NextToken(queryItem, delimiter) do
  246. begin
  247. if queryItem <> '' then
  248. begin
  249. Convert_ESC_Chars;
  250. FVariables.Add(queryItem);
  251. end;
  252. end;
  253. end;
  254. procedure TEZcgi.OutputError(errorMessage : String);
  255. begin
  256. WriteContent('text/html');
  257. writeln('<html><head><title>CGI ERROR</title></head>');
  258. writeln('<body>');
  259. writeln('<center><hr><h1>CGI ERROR</h1><hr></center><br><br>');
  260. writeln('This CGI application encountered the following error: <br>');
  261. writeln('<ul><br>');
  262. writeln('<li> error: ',errorMessage,'<br><hr>');
  263. writeln('<h5><p><i>Notify ',FName,' <a href="mailto:',FEmail,'">',FEmail,'</a></i></p></h5>');
  264. writeln('</body></html>');
  265. Raise ECGIException.Create(errorMessage);
  266. end;
  267. procedure TEZcgi.InitToken(aStr, aSep : String);
  268. begin
  269. aString := aStr;
  270. aSepStr := aSep;
  271. aPos := 1;
  272. aLenStr := Length(aString);
  273. aLenSep := Length(aSepStr);
  274. end;
  275. function TEZcgi.NextToken(var aToken : String; var aSepChar : Char) : Boolean;
  276. var
  277. i : Integer;
  278. j : Integer;
  279. BoT : Integer;
  280. EoT : Integer;
  281. isSep : Boolean;
  282. begin
  283. BoT := aPos;
  284. EoT := aPos;
  285. for i := aPos to aLenStr do
  286. begin
  287. IsSep := false;
  288. for j := 1 to aLenSep do
  289. begin
  290. if aString[i] = aSepStr[j] then
  291. begin
  292. IsSep := true;
  293. Break;
  294. end;
  295. end;
  296. if IsSep then
  297. begin
  298. EoT := i;
  299. aPos := i + 1;
  300. aSepChar := aString[i];
  301. Break;
  302. end
  303. else
  304. begin
  305. if i = aLenStr then
  306. begin
  307. EoT := i;
  308. aPos := i;
  309. Break;
  310. end;
  311. end;
  312. end;
  313. if aPos < aLenStr then
  314. begin
  315. aToken := Copy(aString, BoT, EoT - BoT);
  316. Result := true;
  317. end
  318. else
  319. begin
  320. if aPos = aLenStr then
  321. begin
  322. aToken := Copy(aString, BoT, EoT - BoT + 1);
  323. Result := true;
  324. aPos := aPos + 1;
  325. end
  326. else
  327. begin
  328. Result := false;
  329. end;
  330. end;
  331. end;
  332. end.