[0001] [0002] [0003] [0004] [0005] [0006] [0007] [0008] [0009] [0010] [0011] [0012] [0013] [0014] [0015] [0016] [0017] [0018] [0019] [0020] [0021] [0022] [0023] [0024] [0025] [0026] [0027] [0028] [0029] [0030] [0031] [0032] [0033] [0034] [0035] [0036] [0037] [0038] [0039] [0040] [0041] [0042] [0043] [0044] [0045] [0046] [0047] [0048] [0049] [0050] [0051] [0052] [0053] [0054] [0055] [0056] [0057] [0058] [0059] [0060] [0061] [0062] [0063] [0064] [0065] [0066] [0067] [0068] [0069] [0070] [0071] [0072] [0073] [0074] [0075] [0076] [0077] [0078] [0079] [0080] [0081] [0082] [0083] [0084] [0085] [0086] [0087] [0088] [0089] [0090] [0091] [0092] [0093] [0094] [0095] [0096] [0097] [0098] [0099] [0100] [0101] [0102] [0103] [0104] [0105] [0106] [0107] [0108] [0109] [0110] [0111] [0112] [0113] [0114] [0115] [0116] [0117] [0118] [0119] [0120] [0121] [0122] [0123] [0124] [0125] [0126] [0127] [0128] [0129] [0130] [0131] [0132] [0133] [0134] [0135] [0136] [0137] [0138] [0139] [0140] [0141] [0142] [0143] [0144] [0145] [0146] [0147] [0148] [0149] [0150] [0151] [0152] [0153] [0154] [0155] [0156] [0157] [0158] [0159] [0160] [0161] [0162] [0163] [0164] [0165] [0166] [0167] [0168] [0169] [0170] [0171] [0172] [0173] [0174] [0175] [0176] [0177] [0178] [0179] [0180] [0181] [0182] [0183] [0184] [0185] [0186] [0187] [0188] [0189] [0190] [0191] [0192] [0193] [0194] [0195] [0196] [0197] [0198] [0199] [0200] [0201] [0202] [0203] [0204] [0205] [0206] [0207] [0208] [0209] [0210] [0211] [0212] [0213] [0214] [0215] [0216] [0217] [0218] [0219] [0220] [0221] [0222] [0223] [0224] [0225] [0226] [0227] [0228] [0229] [0230] [0231] [0232] [0233] [0234] [0235] [0236] [0237] [0238] [0239] [0240] [0241] [0242] [0243] [0244] [0245] [0246] [0247] [0248] [0249] [0250] [0251] [0252] [0253] [0254] [0255] [0256] [0257] [0258] [0259] [0260] [0261] [0262] [0263] [0264] [0265] [0266] [0267] [0268] [0269] [0270] [0271] [0272] [0273] [0274] [0275] [0276] [0277] [0278] [0279] [0280] [0281] [0282] [0283] [0284] [0285] [0286] [0287] [0288] [0289] [0290] [0291] [0292] [0293] [0294] [0295] [0296] [0297] [0298] [0299] [0300] [0301] [0302] [0303] [0304] [0305] [0306] [0307] [0308] [0309] [0310] [0311] [0312] [0313] [0314] [0315] [0316] [0317] [0318] [0319] [0320] [0321] [0322] [0323] [0324] [0325] [0326] [0327] [0328] [0329] [0330] [0331] [0332] [0333] [0334] [0335] [0336] [0337] [0338] [0339] [0340] [0341] [0342] [0343] [0344] [0345] [0346] [0347] [0348] [0349] [0350] [0351] [0352] [0353] [0354] [0355] [0356] [0357] [0358] [0359] [0360] [0361] [0362] [0363] [0364] [0365] [0366] [0367] [0368] [0369] [0370] [0371] [0372] [0373] [0374] [0375] [0376] [0377] [0378] [0379] [0380] [0381] [0382] [0383] [0384] [0385] [0386] [0387] [0388] [0389] [0390] [0391] [0392] [0393] [0394] [0395] [0396] [0397] [0398] [0399] [0400] [0401] [0402] [0403] [0404] [0405] [0406] [0407] [0408] [0409] [0410] [0411] [0412] [0413] [0414] [0415] [0416] [0417] [0418] [0419] [0420] [0421] [0422] [0423] [0424] [0425] [0426] [0427] [0428] [0429] [0430] [0431] [0432] [0433] [0434] [0435] [0436] [0437] [0438] [0439] [0440] [0441] [0442] [0443] [0444] [0445] [0446] [0447] [0448] [0449] [0450] [0451] [0452] [0453] [0454] [0455] [0456] [0457] [0458] [0459] [0460] [0461] [0462] [0463] [0464] [0465] [0466] [0467] [0468] [0469] [0470] [0471] [0472] [0473] [0474] [0475] [0476] [0477] [0478] [0479] [0480] [0481] [0482] [0483] [0484] [0485] [0486] [0487] [0488] [0489] [0490] [0491] [0492] [0493] [0494] [0495] [0496] [0497] [0498] [0499] [0500] [0501] [0502] [0503] [0504] [0505] [0506] [0507] [0508] [0509] [0510] [0511] [0512] [0513] [0514] [0515] [0516] [0517] [0518] [0519] [0520] [0521] [0522] [0523] [0524] [0525] [0526] [0527] [0528] [0529] [0530] [0531] [0532] [0533] [0534] [0535]
// CGIplus.java // Compile using: $ javac "CGIplus.java" // // The Java may be a bit brain-dead ... still very much the novice! // This class can be expected to undergo refinement as expertise grows. // Developed using the first-release JDK1.1 beta kit for OpenVMS Alpha. import java.io.*; import java.util.Vector; /** * This class allows CGI scripting to be supported relatively simply using * Java. Relies on the WASD HTTPd CGIplus environment providing the CGI * variables in a data stream rather than as process environment variables. * <P> * Hence scripts must be activated via mapping rules that execute them within * the CGIplus environment, although the scripts themselves do not have to * "persist" in the usual CGIplus script manner, just "System.exit(0)" when * finished processing (at the slight cost of destroying the subprocess). * <P> * Currently supports GET and "x-www-form-urlencoded" POST HTTP method scripts. * Will be expanded to more fully support POST processing as time permits. * * @version 1.0.0, 09-DEC-1997 * @author MGD */ public class CGIplus { private static int usageCount = 0, cgiVarCount = 0, formFieldCount = 0, nextCgiVarCount = 0, nextFormFieldCount = 0; private static FileReader bodyf = null; private static BufferedReader cgipi = null, bodys = null; private static Vector cgiVariables = null, urlEncodedForm = null; private static String cgiPlusEof = null, requestBody = null, line = null; ////////////////////////////////////////////////////////////////////////////// /** * Begin script processing, and synchronize persistant (CGIplus) scripts. * For the first call checks for the CGIPLUSEOF string (if not available * reports it as an error and exits), then opens the CGIPLUSIN stream. * Wait for a request to become available (first record read and discarded). * Then read series of records up until the first empty record (indicates end * of CGI variables), placing each of these records into a vector object for * later search and retrieval. * * @param none * @return none * @exception none */ public void begin () { if (cgiPlusEof == null) { // get the end-of-file (script output) marker cgiPlusEof = System.getProperty("cgipluseof"); if (cgiPlusEof == null) { System.out.println("Sorry ... from a CGIplus environment only!"); System.exit(0); } try { cgipi = new BufferedReader(new FileReader("CGIPLUSIN:")); } catch (Exception e) { e.printStackTrace(); System.exit(0); } } try { // synchronize on the first line and discard line = cgipi.readLine(); // get rest of CGI variables cgiVariables = new Vector(32); cgiVarCount = 0; while ((line = cgipi.readLine()) != null) { if (line.equals("")) break; cgiVariables.addElement(line); cgiVarCount++; } } catch (Exception e) { e.printStackTrace(); System.exit(0); } usageCount++; } ////////////////////////////////////////////////////////////////////////////// /** * Conclude the response. * Write the CGIplus end-of-output value to the server. * Release any resources not relevant to next request (if CGIplus). * * @param none * @return none * @exception none */ public void end () { if (cgiPlusEof == null) begin(); System.out.print(cgiPlusEof); // allow the major resources to be GCed while quiescent (if CGIplus) cgiVariables = null; urlEncodedForm = null; // close input streams if (bodys != null) { try { bodys.close(); } catch (Exception e) { e.printStackTrace(); System.exit(0); } bodys = null; } if (bodyf != null) { try { bodyf.close(); } catch (Exception e) { e.printStackTrace(); System.exit(0); } bodyf = null; } } ////////////////////////////////////////////////////////////////////////////// /** * Return the number of times the script has been used. * Relevant only when behaving as a persistant, CGIplus script. * * @param none * @return none * @exception none */ public int getUsageCount () { return usageCount; } ////////////////////////////////////////////////////////////////////////////// /** * Return the value of the specified CGI variable name. * It is less expensive but not mandatory to supply the the "WWW_" prefix. * Returns null if the specified variable name does not exist. * * @param varName the name of the CGI variable * @return the CGI variable string * @exception none */ public String getCgiVar (String varName) { int cnt; if (cgiPlusEof == null) begin(); if (!varName.startsWith("WWW_")) { varName = "WWW_" + varName; } for (cnt = 0; cnt < cgiVariables.size(); cnt++) { line = (String)cgiVariables.elementAt(cnt); if (!line.startsWith(varName+"=")) continue; return line.substring(varName.length()+1); } return null; } ////////////////////////////////////////////////////////////////////////////// /** * Successive calls return each CGI variable 'name=value' pair. * Returns null and resets when the CGI variables are exhausted. * * @param none * @return the 'name=value' string * @exception none */ public String nextCgiVar () { if (cgiPlusEof == null) begin(); if (nextCgiVarCount >= cgiVariables.size()) { nextCgiVarCount = 0; return null; } return (String)cgiVariables.elementAt(nextCgiVarCount++); } ////////////////////////////////////////////////////////////////////////////// /** * Return the number of CGI variables available. * * @param none * @return count of CGI variables * @exception none */ public int getCgiVarCount () { if (cgiPlusEof == null) begin(); return cgiVarCount; } ////////////////////////////////////////////////////////////////////////////// /** * Output all CGI variable 'name=value' pairs (for debugging, etc.) * * @param none * @return none * @exception none */ public void dumpCgiVar () { int cnt; if (cgiPlusEof == null) begin(); for (cnt = 0; cnt < cgiVariables.size(); cnt++) { System.out.print((String)cgiVariables.elementAt(cnt)); } } ////////////////////////////////////////////////////////////////////////////// /** * Returns the MIME content-type of the body of a POSTed request. * * @param none * @return body content-type * @exception none */ public String getContentType () { return getCgiVar("WWW_CONTENT_TYPE"); } ////////////////////////////////////////////////////////////////////////////// /** * Tests whether the request is POSTed and "www-form-urlencoded". * * @param none * @return true or false * @exception none */ public boolean isPOSTedForm () { String type, method; method = getCgiVar("WWW_REQUEST_METHOD"); if (!method.equals("POST")) return false; type = getCgiVar("WWW_CONTENT_TYPE"); return type.equals("application/x-www-form-urlencoded"); } ////////////////////////////////////////////////////////////////////////////// /** * Returns the content-length of the body for a POSTed request. * * @param none * @return length of POSTed body * @exception none */ public int getContentLength () { return Integer.parseInt(getCgiVar("WWW_CONTENT_LENGTH")); } ////////////////////////////////////////////////////////////////////////////// /** * Read a line from the body of the request (for POSTed requests). * * @param none * @return the line (or null if body exhausted) * @exception none */ public String readBodyLine () { if (bodys == null) { try { bodys = new BufferedReader(bodyf = new FileReader("HTTP$INPUT:")); } catch (Exception e) { e.printStackTrace(); System.exit(0); } } try { return bodys.readLine(); } catch (Exception e) { e.printStackTrace(); System.exit(0); } return null; } ////////////////////////////////////////////////////////////////////////////// /** * Output all lines in request body (for debugging POSTed requests, etc.) * * @param none * @return none * @exception none */ public void dumpBody () { if (cgiPlusEof == null) begin(); while ((line = readBodyLine()) != null) System.out.print(line); } ////////////////////////////////////////////////////////////////////////////// /** * Read entire request body into 'field=value' pairs. * For a 'x-www-form-urlencoded', POSTed request. * * @param none * @return none * @exception none */ // forms tend to be on the small side, read entire body into one string! private void readUrlEncodedForm () { int idx1, idx2; if (cgiPlusEof == null) begin(); line = getContentType(); if (!line.equals("application/x-www-form-urlencoded")) { System.out.println("Not an \"application/x-www-form-urlencoded\" request."); System.exit(0); } requestBody = ""; while ((line = readBodyLine()) != null) { requestBody += line; } urlEncodedForm = new Vector(32); for (idx1 = idx2 = 0;;) { idx2 = requestBody.indexOf('&',idx1); if (idx2 >= 0) { line = urlDecode(requestBody.substring(idx1,idx2)); urlEncodedForm.addElement(line); idx1 = idx2 + 1; formFieldCount++; } else { if (idx1 < requestBody.length()) { line = urlDecode(requestBody.substring(idx1)); urlEncodedForm.addElement(line); formFieldCount++; } break; } } } ////////////////////////////////////////////////////////////////////////////// /** * Output all form 'field=value' pairs (for debugging, etc). * For a 'x-www-form-urlencoded', POSTed request. * * @param none * @return the 'name=value' string * @exception none */ public void dumpForm () { int cnt; if (urlEncodedForm == null) readUrlEncodedForm(); if (urlEncodedForm == null) return; for (cnt = 0; cnt < urlEncodedForm.size(); cnt++) { System.out.print((String)urlEncodedForm.elementAt(cnt)); } } ////////////////////////////////////////////////////////////////////////////// /** * Return the value of the specified form field name. * Returns null if the specified field name does not exist. * For a 'x-www-form-urlencoded', POSTed request. * * @param fieldName the name of the field * @return the (URL-decoded) field value * @exception none */ public String getFormField (String fieldName) { int cnt; if (cgiPlusEof == null) begin(); if (urlEncodedForm == null) readUrlEncodedForm(); for (cnt = 0; cnt < urlEncodedForm.size(); cnt++) { line = (String)urlEncodedForm.elementAt(cnt); if (!line.startsWith(fieldName+"=")) continue; return line.substring(fieldName.length()+1); } return null; } ////////////////////////////////////////////////////////////////////////////// /** * Successive calls return each form field 'name=value' pair. * Returns null and resets when the form fields are exhausted. * For a 'x-www-form-urlencoded', POSTed request. * * @param none * @return the 'name=value' string * @exception none */ public String nextFormField () { if (cgiPlusEof == null) begin(); if (urlEncodedForm == null) readUrlEncodedForm(); if (nextFormFieldCount >= urlEncodedForm.size()) { nextFormFieldCount = 0; return null; } return (String)urlEncodedForm.elementAt(nextFormFieldCount++); } ////////////////////////////////////////////////////////////////////////////// /** * Return the number of form fields. * For a 'x-www-form-urlencoded', POSTed request. * * @param none * @return count of fields in request * @exception none */ public int getFormFieldCount () { if (cgiPlusEof == null) begin(); if (urlEncodedForm == null) readUrlEncodedForm(); if (urlEncodedForm == null) return 0; return formFieldCount; } ////////////////////////////////////////////////////////////////////////////// /** * Decode the supplied URL-encoded string. * Converts '+' into ' ' and "%nn" hex-encoded values into their ASCII * characters. * * @param estr the url-encoded string * @return the decoded string * @exception none */ public String urlDecode (String estr) { int ridx, eidx; char ch; char[] dstr; if (estr == null) return null; dstr = new char[estr.length()]; ridx = 0; for (eidx = 0; eidx < estr.length(); eidx++) { ch = estr.charAt(eidx); if (ch == '+') { dstr[ridx++] = ' '; } else if (ch == '%') { try { dstr[ridx++] = (char) Integer.parseInt(estr.substring(eidx+1,eidx+3), 16); eidx += 2; } catch (NumberFormatException e) { System.out.println(estr.substring(eidx+1,eidx+3) + " is an invalid hexadecimal code"); e.printStackTrace(); System.exit(0); } } else { dstr[ridx++] = ch; } } return String.valueOf(dstr,0,ridx); } ////////////////////////////////////////////////////////////////////////////// }