cgiapp.pp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515
  1. {
  2. $Id$
  3. This file is part of the Free Pascal run time library.
  4. Copyright (c) 1999-2000 by the Free Pascal development team
  5. TCGIApplication class.
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. **********************************************************************}
  12. {$mode objfpc}
  13. {$H+}
  14. unit cgiapp;
  15. Interface
  16. uses
  17. CustApp,Classes,SysUtils;
  18. Const
  19. CGIVarCount = 23;
  20. Type
  21. TCGIVarArray = Array[1..CGIVarCount] of String;
  22. Const
  23. CgiVarNames : TCGIVarArray =
  24. ('AUTH_TYPE',
  25. 'CONTENT_LENGTH',
  26. 'CONTENT_TYPE',
  27. 'GATEWAY_INTERFACE',
  28. 'PATH_INFO',
  29. 'PATH_TRANSLATED',
  30. 'QUERY_STRING', 'REMOTE_ADDR',
  31. 'REMOTE_HOST',
  32. 'REMOTE_IDENT',
  33. 'REMOTE_USER',
  34. 'REQUEST_METHOD',
  35. 'SCRIPT_NAME',
  36. 'SERVER_NAME',
  37. 'SERVER_PORT',
  38. 'SERVER_PROTOCOL',
  39. 'SERVER_SOFTWARE',
  40. 'HTTP_ACCEPT',
  41. 'HTTP_ACCEPT_CHARSET',
  42. 'HTTP_ACCEPT_ENCODING',
  43. 'HTTP_IF_MODIFIED_SINCE',
  44. 'HTTP_REFERER',
  45. 'HTTP_USER_AGENT');
  46. Type
  47. TCgiApplication = Class(TCustomApplication)
  48. Private
  49. FEmail : String;
  50. FAdministrator : String;
  51. FContentTypeEmitted : Boolean;
  52. FCGIVars : TCGIVarArray;
  53. FRequestVars : TStrings;
  54. Function GetCGIVar (Index : Integer) : String;
  55. Procedure InitCGIVars;
  56. Procedure InitRequestVars;
  57. Procedure InitPostVars;
  58. Procedure InitGetVars;
  59. Procedure SetContentLength (Value : Integer);
  60. Procedure SetCGIVar(Index : Integer; Value : String);
  61. Function GetContentLength : Integer;
  62. Function GetServerPort : Word;
  63. Function GetEmail : String;
  64. Function GetAdministrator : String;
  65. Procedure ProcessQueryString(Const FQueryString : String);
  66. Function GetRequestVariable(Const VarName : String) : String;
  67. Function GetRequestVariableCount : Integer;
  68. Public
  69. Constructor Create(AOwner : TComponent); override;
  70. Destructor Destroy; override;
  71. Procedure Initialize; override;
  72. Procedure GetCGIVarList(List : TStrings);
  73. Procedure GetRequestVarList(List : TStrings);
  74. Procedure GetRequestVarList(List : TStrings; NamesOnly : Boolean);
  75. Procedure ShowException(E: Exception);override;
  76. Function EmitContentType : Boolean;
  77. Property AuthType : String Index 1 Read GetCGIVar;
  78. Property ContentLength : Integer Read GetContentLength Write SetContentLength; // Index 2
  79. Property ContentType : String Index 3 Read GetCGIVar Write SetCGIVar;
  80. Property GatewayInterface : String Index 4 Read GetCGIVar;
  81. Property PathInfo : String index 5 read GetCGIvar;
  82. Property PathTranslated : String Index 6 read getCGIVar;
  83. Property QueryString : String Index 7 read getcgivar;
  84. Property RemoteAddress : String Index 8 read GetCGIVar;
  85. Property RemoteHost : String Index 9 read GetCGIVar;
  86. Property RemoteIdent : String Index 10 read GetCGIVar;
  87. Property RemoteUser : String Index 11 read GetCGIVar;
  88. Property RequestMethod : String Index 12 read GetCGIVar;
  89. Property ScriptName : String Index 13 read GetCGIVar;
  90. Property ServerName : String Index 14 read GetCGIVar;
  91. Property ServerPort : Word Read GetServerPort; // Index 15
  92. Property ServerProtocol : String Index 16 read GetCGIVar;
  93. Property ServerSoftware : String Index 17 read GetCGIVar;
  94. Property HTTPAccept : String Index 18 read GetCGIVar;
  95. Property HTTPAcceptCharset : String Index 19 read GetCGIVar;
  96. Property HTTPAcceptEncoding : String Index 20 read GetCGIVar;
  97. Property HTTPIfModifiedSince : String Index 21 read GetCGIVar; // Maybe change to TDateTime ??
  98. Property HTTPReferer : String Index 22 read GetCGIVar;
  99. Property HTTPUserAgent : String Index 23 read GetCGIVar;
  100. Property Email : String Read GetEmail Write FEmail;
  101. Property Administrator : String Read GetAdministrator Write FAdministrator;
  102. Property RequestVariables[VarName : String] : String Read GetRequestVariable;
  103. Property RequestVariableCount : Integer Read GetRequestVariableCount;
  104. end;
  105. ResourceString
  106. SWebMaster = 'webmaster';
  107. SCGIError = 'CGI Error';
  108. SAppEncounteredError = 'The application encountered the following error:';
  109. SError = 'Error: ';
  110. SNotify = 'Notify: ';
  111. SErrNoContentLength = 'No content length passed from server!';
  112. SErrUnsupportedContentType = 'Unsupported content type: "%s"';
  113. SErrNoRequestMethod = 'No REQUEST_METHOD passed from server.';
  114. SErrInvalidRequestMethod = 'Invalid REQUEST_METHOD passed from server.';
  115. Implementation
  116. Constructor TCgiApplication.Create(AOwner : TComponent);
  117. begin
  118. Inherited Create(AOwner);
  119. FRequestVars:=TStringList.Create;
  120. end;
  121. Destructor TCgiApplication.Destroy;
  122. begin
  123. FRequestVars.Free;
  124. Inherited;
  125. end;
  126. Function TCgiApplication.GetCGIVar (Index : Integer) : String;
  127. begin
  128. Result:=FCGIVars[Index];
  129. end;
  130. Procedure TCgiApplication.InitCGIVars;
  131. Var
  132. I : Integer;
  133. L : TStrings;
  134. begin
  135. L:=TStringList.Create;
  136. Try
  137. GetEnvironmentList(L);
  138. For I:=1 to CGIVarCount do
  139. FCGIVars[i]:=L.Values[CGIVarNames[i]];
  140. Finally
  141. L.Free;
  142. end;
  143. end;
  144. Procedure TCgiApplication.Initialize;
  145. begin
  146. StopOnException:=True;
  147. Inherited;
  148. InitCGIVars;
  149. InitRequestVars;
  150. end;
  151. Procedure TCgiApplication.GetCGIVarList(List : TStrings);
  152. Var
  153. I : Integer;
  154. begin
  155. List.Clear;
  156. For I:=1 to cgiVarCount do
  157. List.Add(CGIVarNames[i]+'='+FCGIVars[i]);
  158. end;
  159. Procedure TCgiApplication.GetRequestVarList(List : TStrings);
  160. begin
  161. GetRequestVarList(List,False);
  162. end;
  163. Procedure TCgiApplication.GetRequestVarList(List : TStrings; NamesOnly : Boolean);
  164. Var
  165. I,J : Integer;
  166. S : String;
  167. begin
  168. List.BeginUpdate;
  169. Try
  170. List.Clear;
  171. // Copy one by one, there may be CR/LF in the variables, causing 'Text' to go wrong.
  172. If Assigned(FRequestVars) then
  173. For I:=0 to FRequestVars.Count-1 do
  174. begin
  175. S:=FRequestVars[i];
  176. If NamesOnly then
  177. begin
  178. J:=Pos('=',S);
  179. If (J>0) then
  180. S:=Copy(S,1,J-1);
  181. end;
  182. List.Add(S);
  183. end;
  184. finally
  185. List.EndUpdate;
  186. end;
  187. end;
  188. Function TCgiApplication.GetContentLength : Integer;
  189. begin
  190. Result:=StrToIntDef(GetCGIVar(2),-1);
  191. end;
  192. Procedure TCgiApplication.SetContentLength (Value : Integer);
  193. begin
  194. SetCGIVar(2,IntToStr(Value));
  195. end;
  196. Procedure TCgiApplication.SetCGIVar(Index : Integer; Value : String);
  197. begin
  198. If Index in [1..cgiVarCount] then
  199. FCGIVars[Index]:=Value;
  200. end;
  201. Function TCgiApplication.GetServerPort : Word;
  202. begin
  203. Result:=StrToIntDef(GetCGIVar(15),0);
  204. end;
  205. Function TCgiApplication.EmitContentType : Boolean;
  206. Var
  207. S: String;
  208. begin
  209. Result:=Not FContentTypeEmitted;
  210. If result then
  211. begin
  212. S:=ContentType;
  213. If (S='') then
  214. S:='text/html';
  215. writeln('Content-Type: ',ContentType);
  216. writeln;
  217. FContentTypeEmitted:=True;
  218. end;
  219. end;
  220. Procedure TCgiApplication.ShowException(E: Exception);
  221. Var
  222. TheEmail : String;
  223. begin
  224. If not FContentTypeEmitted then
  225. begin
  226. ContentType:='text/html';
  227. EmitContentType;
  228. end;
  229. If (ContentType='text/html') then
  230. begin
  231. writeln('<html><head><title>',Title,SCGIError,'</title></head>');
  232. writeln('<body>');
  233. writeln('<center><hr><h1>',Title,': ERROR</h1><hr></center><br><br>');
  234. writeln(SAppEncounteredError ,'<br>');
  235. writeln('<ul>');
  236. writeln('<li>',SError,' <b>',E.Message,'</b></ul><hr>');
  237. TheEmail:=Email;
  238. If (TheEmail<>'') then
  239. writeln('<h5><p><i>',SNotify,Administrator,': <a href="mailto:',TheEmail,'">',TheEmail,'</a></i></p></h5>');
  240. writeln('</body></html>');
  241. end;
  242. end;
  243. Function TCgiApplication.GetEmail : String;
  244. Var
  245. H : String;
  246. begin
  247. If (FEmail='') then
  248. begin
  249. H:=ServerName;
  250. If (H<>'') then
  251. Result:=Administrator+'@'+H
  252. else
  253. Result:='';
  254. end
  255. else
  256. Result:=Email;
  257. end;
  258. Function TCgiApplication.GetAdministrator : String;
  259. begin
  260. If (FADministrator<>'') then
  261. Result:=FAdministrator
  262. else
  263. Result:=SWebMaster;
  264. end;
  265. Procedure TCgiApplication.InitRequestVars;
  266. var
  267. R : String;
  268. begin
  269. R:=RequestMethod;
  270. if (R='') then
  271. Raise Exception.Create(SErrNoRequestMethod);
  272. if CompareText(R,'POST')=0 then
  273. InitPostVars
  274. else if CompareText(R,'GET')=0 then
  275. InitGetVars
  276. else
  277. Raise Exception.CreateFmt(SErrInvalidRequestMethod,[R]);
  278. end;
  279. Procedure TCgiApplication.InitPostVars;
  280. var
  281. FQueryString : String;
  282. i : Integer;
  283. ch : Char;
  284. begin
  285. if (FCGIVars[2]='') then
  286. Raise Exception.Create(SErrNoContentLength);
  287. if CompareText(ContentType,'APPLICATION/X-WWW-FORM-URLENCODED')<>0 then
  288. Raise Exception.CreateFmt(SErrUnsupportedContentType,[ContentType]);
  289. SetLength(FQueryString,ContentLength);
  290. for I:= 1 to contentLength Do
  291. begin
  292. Read(ch);
  293. FQueryString[i]:=ch;
  294. end;
  295. ProcessQueryString(FQueryString);
  296. end;
  297. Procedure TCgiApplication.InitGetVars;
  298. Var
  299. FQueryString : String;
  300. begin
  301. FQueryString:=QueryString;
  302. If (FQueryString<>'') then
  303. ProcessQueryString(FQueryString);
  304. end;
  305. const
  306. hexTable = '0123456789ABCDEF';
  307. Procedure TCgiApplication.ProcessQueryString(Const FQueryString : String);
  308. var
  309. queryItem : String;
  310. delimiter : Char;
  311. aString : String;
  312. aSepStr : String;
  313. aPos : Integer;
  314. aLenStr : Integer;
  315. aLenSep : Integer;
  316. function hexConverter(h1, h2 : Char) : Char;
  317. var
  318. B : Byte;
  319. begin
  320. B:=(Pos(upcase(h1),hexTable)-1)*16;
  321. B:=B+Pos(upcase(h2),hexTable)-1;
  322. Result:=chr(B);
  323. end;
  324. procedure Convert_ESC_Chars;
  325. var
  326. index : Integer;
  327. begin
  328. For Index:=1 to Length(QueryItem) do
  329. Index:=Length(QueryItem);
  330. While (Index>0) do
  331. begin
  332. If QueryItem[Index]='+' then
  333. QueryItem[Index]:=' '
  334. else If (QueryItem[Index]='%') and (Index<Length(QueryItem)-1) then
  335. begin
  336. QueryItem[Index]:=hexConverter(QueryItem[Index+1],QueryItem[index+2]);
  337. System.Delete(QueryItem,Index+1,2);
  338. end;
  339. dec(Index);
  340. end;
  341. end;
  342. procedure InitToken(aStr, aSep : String);
  343. begin
  344. aString := aStr;
  345. aSepStr := aSep;
  346. aPos := 1;
  347. aLenStr := Length(aString);
  348. aLenSep := Length(aSepStr);
  349. end;
  350. function NextToken(var aToken : String; var aSepChar : Char) : Boolean;
  351. var
  352. i : Integer;
  353. j : Integer;
  354. BoT : Integer;
  355. EoT : Integer;
  356. isSep : Boolean;
  357. begin
  358. BoT:=aPos;
  359. EoT:=aPos;
  360. for i:=aPos to aLenStr do
  361. begin
  362. IsSep := false;
  363. for j := 1 to aLenSep do
  364. begin
  365. if aString[i] = aSepStr[j] then
  366. begin
  367. IsSep := true;
  368. Break;
  369. end;
  370. end;
  371. if IsSep then
  372. begin
  373. EoT := i;
  374. aPos := i + 1;
  375. aSepChar := aString[i];
  376. Break;
  377. end
  378. else
  379. begin
  380. if i = aLenStr then
  381. begin
  382. EoT := i;
  383. aPos := i;
  384. Break;
  385. end;
  386. end;
  387. end;
  388. if aPos < aLenStr then
  389. begin
  390. aToken := Copy(aString, BoT, EoT - BoT);
  391. Result := true;
  392. end
  393. else
  394. begin
  395. if aPos = aLenStr then
  396. begin
  397. aToken := Copy(aString, BoT, EoT - BoT + 1);
  398. Result := true;
  399. aPos := aPos + 1;
  400. end
  401. else
  402. begin
  403. Result := false;
  404. end;
  405. end;
  406. end;
  407. begin
  408. InitToken(FQueryString, '&');
  409. while NextToken(QueryItem, delimiter) do
  410. begin
  411. if (QueryItem<>'') then
  412. begin
  413. Convert_ESC_Chars;
  414. FRequestVars.Add(QueryItem);
  415. end;
  416. end;
  417. end;
  418. Function TCGIApplication.GetRequestVariable(Const VarName : String) : String;
  419. begin
  420. If Assigned(FRequestVars) then
  421. Result:=FRequestVars.Values[VarName];
  422. end;
  423. Function TCGIApplication.GetRequestVariableCount : Integer;
  424. begin
  425. If Assigned(FRequestVars) then
  426. Result:=FRequestVars.Count
  427. else
  428. Result:=0;
  429. end;
  430. end.