ezcgi.pp 8.9 KB

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