ezcgi.pp 8.3 KB

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