ezcgi.pp 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  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. { *********** Include OS-dependent Getenv Call ************ }
  65. {$I ezcgi.inc}
  66. { *********** Public Methods *************** }
  67. constructor TEZcgi.Create;
  68. begin
  69. FName := 'No name available';
  70. FEmail := 'Email address unavailable';
  71. FVariables := TStringList.Create;
  72. LoadEnvVariables;
  73. end;
  74. destructor TEZcgi.Destroy;
  75. begin
  76. FVariables.Free;
  77. end;
  78. procedure TEZcgi.Run;
  79. begin
  80. ProcessRequest;
  81. end;
  82. procedure TEZcgi.DoPost;
  83. begin
  84. // Must be overriden by child class
  85. end;
  86. procedure TEZcgi.DoGet;
  87. begin
  88. // Must be overriden by child class
  89. end;
  90. procedure TEZcgi.WriteContent(ctype : String);
  91. begin
  92. writeln('Content-Type: ',ctype);
  93. writeln;
  94. end;
  95. procedure TEZcgi.PutLine(sOut : String);
  96. begin
  97. writeln(sOut);
  98. end;
  99. function TEZcgi.GetValue(Index, defaultValue : String) : String;
  100. begin
  101. result := GetVal(Index);
  102. if result = '' then
  103. result := defaultValue;
  104. end;
  105. { *********** Private Methods *************** }
  106. procedure TEZcgi.LoadEnvVariables;
  107. procedure GetEData(variable : String);
  108. var
  109. tempStr : String;
  110. begin
  111. // This is a system dependent call !!
  112. tempStr := GetEnv(variable);
  113. if tempStr <> '' then
  114. FVariables.Add(variable + '=' + tempStr);
  115. end;
  116. begin
  117. { Standard CGI Environment Variables }
  118. GetEData('AUTH_TYPE');
  119. GetEData('CONTENT_LENGTH');
  120. GetEData('CONTENT_TYPE');
  121. GetEData('GATEWAY_INTERFACE');
  122. GetEData('PATH_INFO');
  123. GetEData('PATH_TRANSLATED');
  124. GetEData('QUERY_STRING');
  125. GetEData('REMOTE_ADDR');
  126. GetEData('REMOTE_HOST');
  127. GetEData('REMOTE_IDENT');
  128. GetEData('REMOTE_USER');
  129. GetEData('REQUEST_METHOD');
  130. GetEData('SCRIPT_NAME');
  131. GetEData('SERVER_NAME');
  132. GetEData('SERVER_PORT');
  133. GetEData('SERVER_PROTOCOL');
  134. GetEData('SERVER_SOFTWARE');
  135. { Standard HTTP Environment Variables }
  136. GetEData('HTTP_ACCEPT');
  137. GetEData('HTTP_ACCEPT_CHARSET');
  138. GetEData('HTTP_ACCEPT_ENCODING');
  139. GetEData('HTTP_IF_MODIFIED_SINCE');
  140. GetEData('HTTP_REFERER');
  141. GetEData('HTTP_USER_AGENT');
  142. end;
  143. procedure TEZcgi.ProcessRequest;
  144. var
  145. request : String;
  146. begin
  147. request := GetVal('REQUEST_METHOD');
  148. if request = '' then
  149. OutputError('No REQUEST_METHOD passed from server!')
  150. else if request = 'POST' then
  151. begin
  152. ReadPostQuery;
  153. DoPost;
  154. end
  155. else if request = 'GET' then
  156. begin
  157. ReadGetQuery;
  158. DoGet;
  159. end
  160. else
  161. OutputError('Invalid REQUEST_METHOD passed from server!');
  162. end;
  163. function TEZcgi.GetVal(Index : String) : String;
  164. begin
  165. result := FVariables.Values[Index];
  166. end;
  167. function TEZcgi.GetName(Index : Integer) : String;
  168. begin
  169. result := FVariables.Names[Index];
  170. end;
  171. function TEZcgi.GetVariable(Index : Integer) : String;
  172. begin
  173. result := FVariables[Index];
  174. end;
  175. function TEZcgi.GetVarCount : Integer;
  176. begin
  177. result := FVariables.Count;
  178. end;
  179. procedure TEZcgi.ReadPostQuery;
  180. var
  181. index : Integer;
  182. ch : Char;
  183. temp : String;
  184. code : Word;
  185. contentLength : Integer;
  186. theType : String;
  187. begin
  188. temp := GetVal('CONTENT_LENGTH');
  189. if Length(temp) > 0 then
  190. begin
  191. Val(temp, contentLength, code);
  192. if code <> 0 then
  193. contentLength := 0;
  194. end;
  195. if contentLength = 0 then
  196. OutputError('No content length passed from server!');
  197. theType := UpperCase(GetVal('CONTENT_TYPE'));
  198. if theType <> 'APPLICATION/X-WWW-FORM-URLENCODED' then
  199. OutputError('No content type passed from server!');
  200. FQueryString := '';
  201. for index := 0 to contentLength-1 do
  202. begin
  203. Read(ch);
  204. FQueryString := FQueryString + ch;
  205. end;
  206. GetQueryItems;
  207. end;
  208. procedure TEZcgi.ReadGetQuery;
  209. begin
  210. FQueryString := GetVal('QUERY_STRING');
  211. if FQueryString = '' then
  212. OutputError('No QUERY_STRING passed from server!');
  213. GetQueryItems;
  214. end;
  215. procedure TEZcgi.GetQueryItems;
  216. var
  217. queryItem : String;
  218. delimiter : Char;
  219. function hexConverter(h1, h2 : Char) : Char;
  220. var
  221. thex : byte;
  222. begin
  223. tHex := (Pos(upcase(h1), hexTable) - 1) * 16;
  224. tHex := tHex + Pos(upcase(h2), hexTable) - 1;
  225. result := chr(thex);
  226. end;
  227. procedure Convert_ESC_Chars;
  228. var
  229. index : Integer;
  230. begin
  231. repeat
  232. index := Pos('+', queryItem);
  233. if index > 0 then
  234. queryItem[index] := Chr(32);
  235. until index = 0;
  236. repeat
  237. index := Pos('%', queryItem);
  238. if index > 0 then
  239. begin
  240. queryItem[index] := hexConverter(queryItem[index + 1], queryItem[index + 2]);
  241. system.Delete(queryItem, index + 1, 2);
  242. end;
  243. until index = 0;
  244. end;
  245. begin
  246. InitToken(FQueryString, '&');
  247. while NextToken(queryItem, delimiter) do
  248. begin
  249. if queryItem <> '' then
  250. begin
  251. Convert_ESC_Chars;
  252. FVariables.Add(queryItem);
  253. end;
  254. end;
  255. end;
  256. procedure TEZcgi.OutputError(errorMessage : String);
  257. begin
  258. WriteContent('text/html');
  259. writeln('<html><head><title>CGI ERROR</title></head>');
  260. writeln('<body>');
  261. writeln('<center><hr><h1>CGI ERROR</h1><hr></center><br><br>');
  262. writeln('This CGI application encountered the following error: <br>');
  263. writeln('<ul><br>');
  264. writeln('<li> error: ',errorMessage,'<br><hr>');
  265. writeln('<h5><p><i>Notify ',FName,' <a href="mailto:',FEmail,'">',FEmail,'</a></i></p></h5>');
  266. writeln('</body></html>');
  267. Raise ECGIException.Create(errorMessage);
  268. end;
  269. procedure TEZcgi.InitToken(aStr, aSep : String);
  270. begin
  271. aString := aStr;
  272. aSepStr := aSep;
  273. aPos := 1;
  274. aLenStr := Length(aString);
  275. aLenSep := Length(aSepStr);
  276. end;
  277. function TEZcgi.NextToken(var aToken : String; var aSepChar : Char) : Boolean;
  278. var
  279. i : Integer;
  280. j : Integer;
  281. BoT : Integer;
  282. EoT : Integer;
  283. isSep : Boolean;
  284. begin
  285. BoT := aPos;
  286. EoT := aPos;
  287. for i := aPos to aLenStr do
  288. begin
  289. IsSep := false;
  290. for j := 1 to aLenSep do
  291. begin
  292. if aString[i] = aSepStr[j] then
  293. begin
  294. IsSep := true;
  295. Break;
  296. end;
  297. end;
  298. if IsSep then
  299. begin
  300. EoT := i;
  301. aPos := i + 1;
  302. aSepChar := aString[i];
  303. Break;
  304. end
  305. else
  306. begin
  307. if i = aLenStr then
  308. begin
  309. EoT := i;
  310. aPos := i;
  311. Break;
  312. end;
  313. end;
  314. end;
  315. if aPos < aLenStr then
  316. begin
  317. aToken := Copy(aString, BoT, EoT - BoT);
  318. Result := true;
  319. end
  320. else
  321. begin
  322. if aPos = aLenStr then
  323. begin
  324. aToken := Copy(aString, BoT, EoT - BoT + 1);
  325. Result := true;
  326. aPos := aPos + 1;
  327. end
  328. else
  329. begin
  330. Result := false;
  331. end;
  332. end;
  333. end;
  334. end.