uncgi.pp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640
  1. unit uncgi;
  2. {
  3. $Id$
  4. UNCGI UNIT 2.0.11
  5. ----------------
  6. }
  7. {$ASSERTIONS ON}
  8. interface
  9. uses
  10. strings
  11. {$IFDEF OS2}
  12. ,DosCalls
  13. {$ENDIF OS2}
  14. ;
  15. {***********************************************************************}
  16. const
  17. maxquery = 100;
  18. uncgi_version = 'UNCGI 2.1.1';
  19. uncgi_year = '1999';
  20. maintainer_name = 'Your Name Here';
  21. maintainer_email= '[email protected]';
  22. Type
  23. cgi_error_proc = procedure (Const Proc,Err : String);
  24. PCgiVar=^TCgiVar;
  25. TCgiVar=Record
  26. Name:PChar;
  27. NbrValues:LongInt;
  28. Value:PPChar
  29. end;
  30. var
  31. cgiEnvC:LongInt;
  32. cgiEnvP:PCgiVar;
  33. get_nodata : boolean;
  34. query_read : Cardinal;
  35. uncgi_error : cgi_error_proc;
  36. {***********************************************************************}
  37. { FUNCTION
  38. This function returns the REQUEST METHOD of the CGI-BIN script
  39. Input - Nothing
  40. Output - [GET|POST]
  41. }
  42. function http_request_method: pchar;
  43. { FUNCTION
  44. This function returns the "referring" page. i.e. the page you followed
  45. the link to this CGI-BIN from
  46. Input - Nothing
  47. Output - [http://somewhere.a.tld]
  48. }
  49. function http_referer: pchar;
  50. { FUNCTION
  51. This function returns the IP address of the client.
  52. Input - Nothing
  53. Output - an internet IP address.
  54. }
  55. function http_remote: pchar;
  56. { FUNCTION
  57. This function returns the users's USER AGENT, the browser name etc.
  58. Input - Nothing
  59. Output - user agent string
  60. }
  61. function http_useragent: pchar;
  62. { FUNCTION
  63. This function returns a value from an id=value pair
  64. Input - The identifier you want the value from
  65. Output - If the identifier was found, the resulting value is
  66. the output, otherwise the output is NIL
  67. }
  68. function get_value(id: pchar): pchar;
  69. { FUNCTION
  70. This function extracts array of values for the
  71. same variable (used in some checkbox forms)
  72. Use like in this example:
  73. v:=get_value('some_id');
  74. while v<>'' do begin
  75. Do_something_with(v);
  76. v:=get_next_value;
  77. end;
  78. }
  79. function get_next_value:PChar;
  80. { PROCEDURE
  81. This procedure writes the content-type to the screen
  82. Input - The content type in MIME format
  83. Output - Nothing
  84. Example - set_content('text/plain');
  85. set_content('text/html');
  86. }
  87. procedure set_content(const ctype: string);
  88. { Function to get the requested URL }
  89. function http_url: pchar;
  90. procedure cgi_init;
  91. procedure cgi_deinit;
  92. implementation
  93. {$IFDEF OS2}
  94. function GetEnv (EnvVar: string): PChar;
  95. var P: PChar;
  96. begin
  97. EnvVar := EnvVar + #0;
  98. if DosScanEnv (@EnvVar [1], P) = 0 then GetEnv := P else GetEnv := nil;
  99. end;
  100. {$ENDIF OS2}
  101. {$ifdef win32}
  102. Var EnvP : PChar;
  103. EnvLen : Longint;
  104. OldExitProc : Pointer;
  105. function GetEnvironmentStrings : pchar; external 'kernel32' name 'GetEnvironmentStringsA';
  106. function FreeEnvironmentStrings(p : pchar) : longbool; external 'kernel32' name 'FreeEnvironmentStringsA';
  107. Procedure FInitWin32CGI;
  108. begin
  109. { Free memory }
  110. FreeMem (EnvP,EnvLen);
  111. ExitProc:=OldExitProc;
  112. end;
  113. Procedure InitWin32CGI;
  114. var s : String;
  115. i,len : longint;
  116. hp,p : pchar;
  117. begin
  118. { Make a local copy of environment}
  119. p:=GetEnvironmentStrings;
  120. hp:=p;
  121. envp:=Nil;
  122. envlen:=0;
  123. while hp[0]<>#0 do
  124. begin
  125. len:=strlen(hp);
  126. hp:=hp+len+1;
  127. EnvLen:=Envlen+len+1;
  128. end;
  129. GetMem(EnvP,Envlen);
  130. Move(P^,EnvP^,EnvLen);
  131. FreeEnvironmentStrings(p);
  132. OldExitProc:=ExitProc;
  133. ExitProc:=@FinitWin32CGI;
  134. end;
  135. Function GetEnv(envvar: string): pchar;
  136. { Getenv that can return environment vars of length>255 }
  137. var s : String;
  138. i,len : longint;
  139. hp : pchar;
  140. begin
  141. s:=Envvar+#0;
  142. getenv:=Nil;
  143. hp:=envp;
  144. while hp[0]<>#0 do
  145. begin
  146. len:=strlen(hp);
  147. i:=Longint(strscan(hp,'='))-longint(hp);
  148. if StrLIComp(@s[1],HP,i-1)=0 then
  149. begin
  150. Len:=Len-i;
  151. getmem (getenv,len);
  152. Move(HP[I+1],getenv^,len+1);
  153. break;
  154. end;
  155. { next string entry}
  156. hp:=hp+len+1;
  157. end;
  158. end;
  159. {$endif}
  160. {$ifdef GO32V2}
  161. Function GetEnv(envvar: string): pchar;
  162. var
  163. hp : ppchar;
  164. hs : string;
  165. eqpos : longint;
  166. begin
  167. envvar:=upcase(envvar);
  168. hp:=envp;
  169. getenv:=nil;
  170. while assigned(hp^) do
  171. begin
  172. hs:=strpas(hp^);
  173. eqpos:=pos('=',hs);
  174. if copy(hs,1,eqpos-1)=envvar then
  175. begin
  176. getenv:=hp^+eqpos;
  177. exit;
  178. end;
  179. inc(hp);
  180. end;
  181. end;
  182. {$endif}
  183. {$ifdef unix}
  184. Function GetEnv(P:string):Pchar;
  185. {
  186. Searches the environment for a string with name p and
  187. returns a pchar to it's value.
  188. A pchar is used to accomodate for strings of length > 255
  189. }
  190. var
  191. ep : ppchar;
  192. found : boolean;
  193. Begin
  194. p:=p+'='; {Else HOST will also find HOSTNAME, etc}
  195. ep:=envp;
  196. found:=false;
  197. if ep<>nil then
  198. begin
  199. while (not found) and (ep^<>nil) do
  200. begin
  201. if strlcomp(@p[1],(ep^),length(p))=0 then
  202. found:=true
  203. else
  204. inc(ep);
  205. end;
  206. end;
  207. if found then
  208. getenv:=ep^+length(p)
  209. else
  210. getenv:=nil;
  211. end;
  212. {$endif unix}
  213. var
  214. done_init : boolean;
  215. procedure set_content(const ctype: string);
  216. begin
  217. writeln('Content-Type: ',ctype);
  218. writeln;
  219. end;
  220. function http_request_method: pchar;
  221. begin
  222. http_request_method :=getenv('REQUEST_METHOD');
  223. end;
  224. function http_referer: pchar;
  225. begin
  226. http_referer :=getenv('HTTP_REFERER');
  227. end;
  228. function http_useragent: pchar;
  229. begin
  230. http_useragent :=getenv('HTTP_USER_AGENT');
  231. end;
  232. function hexconv(h1,h2: char): char;
  233. function h2c(c:char):byte;
  234. begin
  235. case c of
  236. '0'..'9':h2c := ord(c) - ord('0');
  237. 'A'..'F':h2c := 10 + (ord(UpCase(c)) - ord('A'));
  238. end;
  239. end;
  240. begin
  241. HexConv:=Chr(h2c(h1)*16+h2c(h2));
  242. end;
  243. procedure def_uncgi_error(const pname,perr: string);
  244. begin
  245. set_content('text/html');
  246. writeln('<html><head><title>UNCGI ERROR</title></head>');
  247. writeln('<body>');
  248. writeln('<center><hr><h1>UNCGI ERROR</h1><hr></center><br><br>');
  249. writeln('UnCgi encountered the following error: <br>');
  250. writeln('<ul><br>');
  251. writeln('<li> procedure: ',pname,'<br>');
  252. writeln('<li> error: ',perr,'<br><hr>');
  253. writeln(
  254. '<h5><p><i>uncgi (c) ',uncgi_year,' ',maintainer_name,
  255. { skelet fix }
  256. '<a href="mailto:',maintainer_email,'">',
  257. maintainer_email,'</a></i></p></h5>');
  258. writeln('</body></html>');
  259. halt;
  260. end;
  261. var
  262. gv_cnt,gv_cnt_n:LongInt;
  263. function get_next_value:PChar;
  264. begin
  265. Assert(done_init,'Please call cgi_init() first');
  266. if gv_cnt>=cgiEnvC
  267. then
  268. Exit(Nil);
  269. with cgiEnvP[gv_cnt] do
  270. begin
  271. if gv_cnt_n>=NbrValues
  272. then
  273. Exit(Nil);
  274. get_next_value:=Value[gv_cnt_n];
  275. end;
  276. Inc(gv_cnt_n);
  277. end;
  278. function get_value(id: pchar): pchar;
  279. begin
  280. Assert(done_init,'Please call cgi_init() first');
  281. gv_cnt:=0;
  282. gv_cnt_n:=0;
  283. while(gv_cnt<cgiEnvC)and(StrComp(id,cgiEnvP[gv_cnt].Name)<>0)do
  284. Inc(gv_cnt);
  285. get_value:=get_next_value;
  286. end;
  287. Function UnEscape(QueryString: PChar): PChar;
  288. var
  289. qunescaped : pchar;
  290. sptr : longint;
  291. cnt : word;
  292. qslen : longint;
  293. begin
  294. qslen:=strlen(QueryString);
  295. if qslen=0 then
  296. begin
  297. Unescape:=Nil;
  298. get_nodata:=true;
  299. exit;
  300. end
  301. else
  302. get_nodata :=false;
  303. { skelet fix }
  304. {Escaped chain is usually longer than the unescaped chain}
  305. GetMem(qunescaped,qslen+1);
  306. if qunescaped=nil
  307. then
  308. uncgi_error('UnEscape()','Could not allocate memory');
  309. sptr :=0;
  310. { for cnt := 0 to qslen do +++++ use while instead of for }
  311. cnt:=0;
  312. while cnt<qslen do
  313. begin
  314. case querystring[cnt] of
  315. '+': qunescaped[sptr]:=' ';
  316. '%': begin
  317. qunescaped[sptr]:=hexconv(querystring[cnt+1], querystring[cnt+2]);
  318. inc(cnt,2); { <--- not allowed in for loops in pascal }
  319. end;
  320. else
  321. qunescaped[sptr] := querystring[cnt];
  322. end;
  323. inc(sptr);
  324. { skelet fix }
  325. qunescaped[sptr]:=#0;
  326. inc(cnt); { <-- don't forget to increment }
  327. end;
  328. UnEscape:=StrNew(qunescaped);
  329. FreeMem(qunescaped,qsLen+1);
  330. end;
  331. Function Chop(QueryString:PChar):Cardinal;
  332. var
  333. VarName,VarValue,name_pos,value_pos:PChar;
  334. sz,EnvCC:LongInt;
  335. p:Pointer;
  336. begin
  337. GetMem(cgiEnvP,MaxQuery*SizeOf(TCgiVar));
  338. name_pos:=QueryString;
  339. value_pos:=QueryString;
  340. repeat
  341. value_pos:=StrScan(name_pos,'=');
  342. if value_pos=Nil
  343. then
  344. value_pos:=StrEnd(name_pos)
  345. else
  346. Inc(value_pos);
  347. sz:=value_pos-name_pos-1;
  348. VarName:=StrAlloc(sz+1);
  349. StrLCopy(VarName,name_pos,sz);
  350. name_pos:=StrScan(name_pos,'&');
  351. if name_pos=Nil
  352. then
  353. sz:=StrLen(value_pos)
  354. else
  355. begin
  356. Inc(name_pos);
  357. sz:=name_pos-value_pos-1;
  358. end;
  359. VarValue:=StrAlloc(sz+1);
  360. StrLCopy(VarValue,value_pos,sz);
  361. EnvCC:=0;
  362. repeat
  363. with cgiEnvP[EnvCC] do
  364. begin
  365. if EnvCC=cgiEnvC
  366. then
  367. begin
  368. if cgiEnvC>=MaxQuery
  369. then
  370. uncgi_error('cgi_read_get_query()','Your are trying to use more than max varaibles allowed! Please change value of "MaxQuery" and recompile your program')
  371. else
  372. begin
  373. Name:=UnEscape(VarName);
  374. GetMem(Value,MaxQuery*SizeOf(PChar));
  375. NbrValues:=0;
  376. Inc(cgiEnvC);
  377. end;
  378. end;
  379. if StrComp(VarName,Name)=0
  380. then
  381. begin
  382. if NbrValues>=MaxQuery
  383. then
  384. uncgi_error('cgi_read_get_query()','Your are trying to use more than max values allowed for a given variable! Please change value of "MaxQuery" and recompile your program')
  385. else
  386. begin
  387. Value[NbrValues]:=UnEscape(VarValue);
  388. Inc(NbrValues);
  389. end;
  390. StrDispose(VarName);
  391. StrDispose(VarValue);
  392. break;
  393. end;
  394. end;
  395. Inc(EnvCC);
  396. until false;
  397. until name_pos=Nil;
  398. for EnvCC:=0 to cgiEnvC-1 do
  399. with cgiEnvP[EnvCC] do
  400. begin
  401. p:=Value;
  402. sz:=NbrValues*SizeOf(PChar);
  403. GetMem(Value,sz);
  404. Move(p^,Value^,sz);
  405. FreeMem(p,MaxQuery*SizeOf(PChar));
  406. end;
  407. p:=cgiEnvP;
  408. sz:=cgiEnvC*SizeOf(TCgiVar);
  409. GetMem(cgiEnvP,sz);
  410. Move(p^,cgiEnvP^,sz);
  411. FreeMem(p,MaxQuery*SizeOf(TCgiVar));
  412. Chop:=Abs(cgiEnvC);
  413. end;
  414. procedure cgi_read_get_query;
  415. var
  416. querystring : pchar;
  417. qslen : longint;
  418. begin
  419. querystring :=strnew(getenv('QUERY_STRING'));
  420. if querystring<>NIL
  421. then
  422. begin
  423. qslen :=strlen(querystring);
  424. if qslen=0
  425. then
  426. begin
  427. get_nodata :=true;
  428. exit;
  429. end
  430. else
  431. get_nodata :=false;
  432. query_read:=Chop(QueryString);
  433. end;
  434. StrDispose(QueryString);
  435. end;
  436. procedure cgi_read_post_query;
  437. var
  438. querystring : pchar;
  439. qslen : longint;
  440. sptr : longint;
  441. clen : string;
  442. ch : char;
  443. begin
  444. if getenv('CONTENT_LENGTH')<>Nil then
  445. begin
  446. clen:=strpas (getenv('CONTENT_LENGTH'));
  447. val(clen,qslen);
  448. if (upcase(strpas(getenv('CONTENT_TYPE')))='APPLICATION/X-WWW-FORM-URLENCODED')
  449. or (upcase(strpas(getenv('CONTENT_TYPE')))='TEXT/PLAIN')
  450. then
  451. begin
  452. getmem(querystring,qslen+1);
  453. sptr :=0;
  454. while sptr<>qslen do
  455. begin
  456. read(ch);
  457. QueryString[sptr]:=ch;
  458. inc(sptr);
  459. end;
  460. { !!! force null-termination }
  461. QueryString[sptr]:=#0;
  462. query_read:=Chop(QueryString);
  463. end;
  464. end;
  465. end;
  466. procedure cgi_init;
  467. var
  468. rmeth : pchar;
  469. begin
  470. Assert(NOT done_init,'cgi_init() was already called');
  471. query_read:=0;
  472. rmeth :=http_request_method;
  473. if rmeth=nil then
  474. begin
  475. uncgi_error('cgi_init()','No REQUEST_METHOD passed from server!');
  476. exit;
  477. end;
  478. if strcomp('POST',rmeth)=0 then cgi_read_post_query else
  479. if strcomp('GET',rmeth)=0 then cgi_read_get_query else
  480. uncgi_error('cgi_init()','No REQUEST_METHOD passed from server!');
  481. done_init :=true;
  482. end;
  483. procedure cgi_deinit;
  484. var
  485. i,j:LongInt;
  486. begin
  487. Assert(done_init,'Please call cgi_init() first');
  488. if cgiEnvC=0
  489. then
  490. Exit;
  491. for i:=0 to cgiEnvC-1 do
  492. with cgiEnvP[i] do
  493. begin
  494. StrDispose(Name);
  495. for j:=0 to NbrValues-1 do
  496. StrDispose(Value[j]);
  497. FreeMem(Value,NbrValues*SizeOf(PChar));
  498. end;
  499. FreeMem(cgiEnvP,cgiEnvC*SizeOf(TCgiVar));
  500. cgiEnvC:=0;
  501. end;
  502. Function http_url: pchar;
  503. begin
  504. http_url:=getenv('REQUEST_URI');
  505. end;
  506. function http_remote: pchar;
  507. begin
  508. http_remote :=getenv('REMOTE_ADDR');
  509. end;
  510. begin
  511. {$ifdef win32}
  512. InitWin32CGI;
  513. {$endif}
  514. uncgi_error:=@def_uncgi_error;
  515. done_init :=false;
  516. end.
  517. {
  518. HISTORY
  519. $Log$
  520. Revision 1.13 2003-09-27 12:07:31 peter
  521. * unix getenv added
  522. Revision 1.12 2003/07/16 12:56:03 mazen
  523. + using Assert to monitor done_init and get state of
  524. un_cgi initailization
  525. * renaming EnvP and EnvC to cgiEnvP and cgiEnvP
  526. to avoid confusion with regular EnvP and EnvC
  527. varaibles especially under win32 target
  528. * set_contents get parameter by address (const)
  529. Revision 1.11 2003/05/29 08:58:45 michael
  530. + Fixed inline error when building
  531. Revision 1.10 2003/05/27 20:50:18 mazen
  532. * New implemtation of HexConv
  533. * New implementation of Chop to fix an incompatibilty
  534. bug with SysUtils.
  535. * Replacing quary_array (static) by EnvP(dynamic)
  536. Revision 1.9 2002/10/24 17:25:36 sg
  537. * Fixed parsing of empty URL arguments (with missing "=")
  538. Revision 1.8 2002/10/18 05:43:53 michael
  539. + Fix of invalid pointer bug in unescape, from U. Maeder
  540. Revision 1.7 2002/10/10 05:48:20 michael
  541. Added http_remote and fixed determining of input method. Fix courtesy of Antal <[email protected]>
  542. Revision 1.6 2002/09/12 16:24:59 michael
  543. + Added http_url function from Michael Weinert
  544. Revision 1.5 2002/09/07 15:43:06 peter
  545. * old logs removed and tabs fixed
  546. Revision 1.4 2002/05/31 11:54:33 marco
  547. * Renamefest for 1.0, many 1.1.x spots patched also.
  548. Revision 1.3 2002/03/04 17:57:17 peter
  549. * updated example in comment
  550. Revision 1.2 2002/03/01 10:57:03 peter
  551. * get_next_value patch from Skelet
  552. Revision 1.1 2002/01/29 17:55:23 peter
  553. * splitted to base and extra
  554. }