[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] [0536] [0537] [0538] [0539] [0540] [0541] [0542] [0543] [0544] [0545] [0546] [0547] [0548] [0549] [0550] [0551] [0552] [0553] [0554] [0555] [0556] [0557] [0558] [0559] [0560] [0561] [0562] [0563] [0564] [0565] [0566] [0567] [0568] [0569] [0570] [0571] [0572] [0573] [0574] [0575] [0576] [0577] [0578] [0579] [0580] [0581] [0582] [0583] [0584] [0585] [0586] [0587] [0588] [0589] [0590] [0591] [0592] [0593] [0594] [0595] [0596] [0597] [0598] [0599] [0600] [0601] [0602] [0603] [0604] [0605] [0606] [0607] [0608] [0609] [0610] [0611] [0612] [0613] [0614] [0615] [0616] [0617] [0618] [0619] [0620] [0621] [0622] [0623] [0624] [0625] [0626] [0627] [0628] [0629] [0630] [0631] [0632] [0633] [0634] [0635] [0636] [0637] [0638] [0639] [0640] [0641] [0642] [0643] [0644] [0645] [0646] [0647] [0648] [0649] [0650] [0651] [0652] [0653] [0654] [0655] [0656] [0657] [0658] [0659] [0660] [0661] [0662] [0663] [0664] [0665] [0666] [0667] [0668] [0669] [0670] [0671] [0672] [0673] [0674] [0675] [0676] [0677] [0678] [0679] [0680] [0681] [0682] [0683] [0684] [0685] [0686] [0687] [0688] [0689] [0690] [0691] [0692] [0693] [0694] [0695] [0696] [0697] [0698] [0699] [0700] [0701] [0702] [0703] [0704] [0705] [0706] [0707] [0708] [0709] [0710] [0711] [0712] [0713] [0714] [0715] [0716] [0717] [0718] [0719] [0720] [0721] [0722] [0723] [0724] [0725] [0726] [0727] [0728] [0729] [0730] [0731] [0732] [0733] [0734] [0735] [0736] [0737] [0738] [0739] [0740] [0741] [0742] [0743] [0744] [0745] [0746] [0747] [0748] [0749] [0750] [0751] [0752] [0753] [0754] [0755] [0756] [0757] [0758] [0759] [0760] [0761] [0762] [0763] [0764] [0765] [0766] [0767] [0768] [0769] [0770] [0771] [0772] [0773] [0774] [0775] [0776] [0777] [0778] [0779] [0780] [0781] [0782] [0783] [0784] [0785] [0786] [0787] [0788] [0789] [0790] [0791] [0792] [0793] [0794] [0795] [0796] [0797] [0798] [0799] [0800] [0801] [0802] [0803] [0804] [0805] [0806] [0807] [0808] [0809] [0810] [0811] [0812] [0813] [0814] [0815] [0816] [0817] [0818] [0819] [0820] [0821] [0822] [0823] [0824] [0825] [0826] [0827] [0828] [0829] [0830] [0831] [0832] [0833] [0834] [0835] [0836] [0837] [0838] [0839] [0840] [0841] [0842] [0843] [0844] [0845] [0846] [0847] [0848] [0849] [0850] [0851] [0852] [0853] [0854] [0855] [0856] [0857] [0858] [0859] [0860] [0861] [0862] [0863] [0864] [0865] [0866] [0867] [0868] [0869] [0870] [0871] [0872] [0873] [0874] [0875] [0876] [0877] [0878] [0879] [0880] [0881] [0882] [0883] [0884] [0885] [0886] [0887] [0888] [0889] [0890] [0891] [0892] [0893] [0894] [0895] [0896] [0897] [0898] [0899] [0900] [0901] [0902] [0903] [0904] [0905] [0906] [0907] [0908] [0909] [0910] [0911] [0912] [0913] [0914] [0915] [0916] [0917] [0918] [0919] [0920] [0921] [0922] [0923] [0924] [0925] [0926] [0927] [0928] [0929] [0930] [0931] [0932] [0933] [0934] [0935] [0936] [0937] [0938] [0939] [0940] [0941] [0942] [0943] [0944] [0945] [0946] [0947] [0948] [0949] [0950] [0951] [0952] [0953] [0954] [0955] [0956] [0957] [0958] [0959] [0960] [0961] [0962] [0963] [0964] [0965] [0966] [0967] [0968] [0969] [0970] [0971] [0972] [0973] [0974] [0975] [0976] [0977] [0978] [0979] [0980] [0981] [0982] [0983] [0984] [0985] [0986] [0987] [0988] [0989] [0990] [0991] [0992] [0993] [0994] [0995] [0996] [0997] [0998] [0999] [1000] [1001] [1002] [1003] [1004] [1005] [1006] [1007] [1008] [1009] [1010] [1011] [1012] [1013] [1014] [1015] [1016] [1017] [1018] [1019] [1020] [1021] [1022] [1023] [1024] [1025] [1026] [1027] [1028] [1029] [1030] [1031] [1032] [1033] [1034] [1035] [1036] [1037] [1038] [1039] [1040] [1041] [1042] [1043] [1044] [1045] [1046] [1047] [1048] [1049] [1050] [1051] [1052] [1053] [1054] [1055] [1056] [1057] [1058] [1059] [1060] [1061] [1062] [1063] [1064] [1065] [1066] [1067] [1068] [1069] [1070] [1071] [1072] [1073] [1074] [1075] [1076] [1077] [1078] [1079] [1080] [1081] [1082] [1083] [1084] [1085] [1086] [1087] [1088] [1089] [1090] [1091] [1092] [1093] [1094] [1095] [1096] [1097] [1098] [1099] [1100] [1101] [1102] [1103] [1104] [1105] [1106] [1107] [1108] [1109] [1110] [1111] [1112] [1113] [1114] [1115] [1116] [1117] [1118] [1119] [1120] [1121] [1122] [1123] [1124] [1125] [1126] [1127] [1128] [1129] [1130] [1131] [1132] [1133] [1134] [1135] [1136] [1137] [1138] [1139] [1140] [1141] [1142] [1143] [1144] [1145] [1146] [1147] [1148] [1149] [1150] [1151] [1152] [1153] [1154] [1155] [1156] [1157] [1158] [1159] [1160] [1161] [1162] [1163] [1164] [1165] [1166] [1167] [1168] [1169] [1170] [1171] [1172] [1173] [1174] [1175] [1176] [1177] [1178] [1179] [1180] [1181] [1182] [1183] [1184] [1185] [1186] [1187] [1188] [1189] [1190] [1191] [1192] [1193] [1194] [1195] [1196] [1197] [1198] [1199] [1200] [1201] [1202] [1203] [1204] [1205] [1206] [1207] [1208] [1209] [1210] [1211] [1212] [1213] [1214] [1215] [1216] [1217] [1218] [1219] [1220] [1221] [1222] [1223] [1224] [1225] [1226] [1227] [1228] [1229] [1230] [1231] [1232] [1233] [1234] [1235] [1236] [1237] [1238] [1239] [1240] [1241] [1242] [1243] [1244] [1245] [1246] [1247] [1248] [1249] [1250] [1251] [1252] [1253] [1254] [1255] [1256] [1257] [1258] [1259] [1260] [1261] [1262] [1263] [1264] [1265] [1266] [1267] [1268] [1269] [1270] [1271] [1272] [1273] [1274] [1275] [1276] [1277] [1278] [1279] [1280] [1281] [1282] [1283] [1284] [1285] [1286] [1287] [1288] [1289] [1290] [1291] [1292] [1293] [1294] [1295] [1296] [1297] [1298] [1299] [1300] [1301] [1302] [1303] [1304] [1305] [1306] [1307] [1308] [1309] [1310] [1311] [1312] [1313] [1314] [1315] [1316] [1317] [1318] [1319] [1320] [1321] [1322] [1323] [1324] [1325] [1326] [1327] [1328] [1329] [1330] [1331] [1332] [1333] [1334] [1335] [1336] [1337] [1338] [1339] [1340] [1341] [1342] [1343] [1344] [1345] [1346] [1347] [1348] [1349] [1350] [1351] [1352] [1353] [1354] [1355] [1356] [1357] [1358] [1359] [1360] [1361] [1362] [1363] [1364] [1365] [1366] [1367] [1368] [1369] [1370] [1371] [1372] [1373] [1374] [1375] [1376] [1377] [1378] [1379] [1380] [1381] [1382] [1383] [1384] [1385] [1386] [1387] [1388] [1389] [1390] [1391] [1392] [1393] [1394] [1395] [1396] [1397] [1398] [1399] [1400] [1401] [1402] [1403] [1404] [1405] [1406] [1407] [1408] [1409] [1410] [1411] [1412] [1413] [1414] [1415] [1416] [1417] [1418] [1419] [1420] [1421] [1422] [1423] [1424] [1425] [1426] [1427] [1428] [1429] [1430] [1431] [1432] [1433] [1434] [1435] [1436] [1437] [1438] [1439] [1440] [1441] [1442] [1443] [1444] [1445] [1446] [1447] [1448] [1449] [1450] [1451] [1452] [1453] [1454] [1455] [1456] [1457] [1458] [1459] [1460] [1461] [1462] [1463] [1464] [1465] [1466] [1467] [1468] [1469] [1470] [1471] [1472] [1473] [1474] [1475] [1476] [1477] [1478] [1479] [1480] [1481] [1482] [1483] [1484] [1485] [1486] [1487] [1488] [1489] [1490] [1491] [1492] [1493] [1494] [1495] [1496] [1497] [1498] [1499] [1500] [1501] [1502] [1503] [1504] [1505] [1506] [1507] [1508] [1509] [1510] [1511] [1512] [1513] [1514] [1515] [1516] [1517] [1518] [1519] [1520] [1521] [1522] [1523] [1524] [1525] [1526] [1527] [1528] [1529] [1530] [1531] [1532] [1533] [1534] [1535] [1536] [1537] [1538] [1539] [1540] [1541] [1542] [1543] [1544] [1545] [1546] [1547] [1548] [1549] [1550] [1551] [1552] [1553] [1554] [1555] [1556] [1557] [1558] [1559] [1560] [1561] [1562] [1563] [1564] [1565] [1566] [1567] [1568] [1569] [1570] [1571] [1572] [1573] [1574] [1575] [1576] [1577] [1578] [1579] [1580] [1581] [1582] [1583] [1584] [1585] [1586] [1587] [1588] [1589] [1590] [1591] [1592] [1593] [1594] [1595] [1596] [1597] [1598] [1599] [1600] [1601] [1602] [1603] [1604] [1605] [1606] [1607] [1608] [1609] [1610] [1611] [1612] [1613] [1614] [1615] [1616] [1617] [1618] [1619] [1620] [1621] [1622] [1623] [1624] [1625] [1626] [1627] [1628] [1629] [1630] [1631] [1632] [1633] [1634] [1635] [1636] [1637] [1638] [1639] [1640] [1641] [1642] [1643] [1644] [1645] [1646] [1647] [1648] [1649] [1650] [1651] [1652] [1653] [1654] [1655] [1656] [1657] [1658] [1659] [1660] [1661] [1662] [1663] [1664] [1665] [1666] [1667] [1668] [1669] [1670] [1671] [1672] [1673] [1674] [1675] [1676] [1677] [1678] [1679] [1680] [1681] [1682] [1683] [1684] [1685] [1686] [1687] [1688] [1689] [1690] [1691] [1692] [1693] [1694] [1695] [1696] [1697] [1698] [1699] [1700] [1701] [1702] [1703] [1704] [1705] [1706] [1707] [1708] [1709] [1710] [1711] [1712] [1713] [1714] [1715] [1716] [1717] [1718] [1719] [1720] [1721] [1722] [1723] [1724] [1725] [1726] [1727] [1728] [1729] [1730] [1731] [1732] [1733] [1734] [1735] [1736] [1737] [1738] [1739] [1740] [1741] [1742] [1743] [1744] [1745] [1746] [1747] [1748] [1749] [1750]
/* * This program is run by the WWWEXEC scriptserver to do pre-processing of * html files, dynamically inserting files or other generated data. * * To specify the preprocessing you must give the file a distinct file type * (e.g. htmlx) and add the following to the configuration file: * * suffix .htmlx text/x-server-parsed-html * presentation text/x-server-parsed-html html_preproc * * WWWEXEC will then execute the following command line: * * html_preproc method url protocol * * argv[1] Method specified in request (e.g. GET). * argv[2] Ident portion of requested URL, after translation by * rule file. See special parsing note below. * argv[3] Protocol specified in request, "" or "HTTP/1.0". * * None of the resulting file contents is returned until the entire file * has been processed. This is required for 2 reasons: * * 1. The HTTP status, which is the first part of the response, is not known * until processing is complete. * * 2. Processing of the file may require using additional server functions, * such as <DNETXLATE>, which become unavailable once the HTTP response * <DNETRAW> is started. * * Argv[2] parsing: * If the path in argv[2] is of the form /dir/filename.partname.type, * then partname is extracted from /dir/filename.type. * * Author: David Jones * Date: 25-AUG-1994 * Revised: 3-SEP-1994 Re-coded parsing. * Revised: 6-OCT-1994 Support fsize/flastmod. * Revised: 11-OCT_1994 Adjust length in translate. * Revised: 20-DEC-1994 Fix length in do_include * Revised: 22-APR-1995 Added extra echo variables suggested by * Kent Covert (kacovert@miavx1.acs.muohio.edu): * DOCUMENT_NAME, LAST_MODIFIED, * ACCESSES/ACCESSES_ORDINAL * Revised: 23-APR-1995 Add permissions file checks to ACCESSES var. * Revised: 25-APR-1995 Overhaul: * - Add strftime() formatting options to * LAST_MODIFIED, DATE_LOCAL, ACCESSES* * - Add version numbering to ACCESSES. * - Rename indexio.h to access_db.h * Revised: 15-MAY-1995 Use <DNETXLATEV> for virtual includes * (does protection check). * Revised: 21-JUN-1995 Remove '\r's from net_link_printf formats as * net_link_printf will add them. * Revised: 8-AUG-1995 Use CGI mode rather than RAW mode (stsline * can be sent using status: CGI header) * Revised: 31-AUG-1995 Fixup formatting of ACCESSES values for * VAXC compatibility. * Revised: 13-NOV-1995 Add tag_verify config option. * Revised: 12-MAY-1996 Convert for MST support, (symbol IS_MST defined * when compiled as part of MST image). * Revised: 18-MAY-1996 Add part include idea of Richard Levitte. * Revised: 19-MAY-1996 Changed to understand multiple instances of * a part. * Revised: 27-MAY-1996 Changed to accept multiple part names in * #begin and #end directives. Separate names * with whitespace, e.g.: * <!--#begin small big --> * (allows you to condense comments around * command parts such as header tags). * Revised: 28-MAY-1996 Bug fix, supply missing argument in open error * messages (MST's only). * Revised: 9-SEP-1996 Change member names to avoid recursive * macro substituition on MST variant. * Revised: 25-OCT-1996 Place limit on size of file to process, * 100,000 for script, 60000 for MST. * Revised: 25-DEC-1996 Split main routine into 2 parts to enable * pre-processor to be called from other * scripts (must define CALLABLE_PREPROC). * Revised: 3-FEB-1997 Fix bug handling directives that return zero * length segment. * Revised: 26-AUG-1997 Add last-modified header if not using counter. * Revised: 8-DEC-1997 Add limited support for nested includes. * (lastmod date is always for primary file). * Revised: 12-DEC-1997 Consolidate file loads into single routine. * Revised: 15-DEC-1997 Save to cache in 4000 byte chunks vs. 16000 * Revised: 28-JAN-1998 Modify parse_part_name to ignore null part * names in the filename, allowing specification * of files with multiple dots: * foo.htmlx file foo.htmlx * foo.bar.htmlx part bar in foo.htmlx * foo.bar.x.htmlx part x in foo.bar.htmlx * foo..htmlx file foo.htmlx * foo.bar..html file foo.bar.htmlx */ #ifndef IS_MST /* * Include files only needed when not an MST */ #include <stdlib.h> #include <stdio.h> #include <string.h> #include <errno.h> #include <ctype.h> #include "scriptlib.h" #include "access_db.h" #define LOCK_C_RTL #define UNLOCK_C_RTL #define INPUT_LIMIT 100000 #else #define INPUT_LIMIT 60000 #endif #include <time.h> #include <syidef.h> #include <descrip.h> #include <stat.h> int LIB$GETSYI(); #if defined(DEBUG) && defined(IS_MST) #error "The combination of DEBUG and IS_MST is not allowed." #endif struct segment { int length; char *addr; }; typedef struct segment *segptr; static int send_http_header ( char *stsline, char *content ); static int parse_directive ( char *path, char **targ, int *outlen, char **outbuf ); static int parse_tag ( char *tag, int maxlen, int *taglen, char **targ ); static int parse_part_name ( char *fname, char **pfile, char **pname ); static int extract_part ( char *, char *, int *, char ** ); static segptr alloc_segment ( segptr seg, int sc, int *seg_size, char *start ); static int tag_verify, abort_status; static char *access_file_fdl = "\ FILE; ORGANIZATION indexed; PROTECTION (system:RWED,owner:RWED,group,world);\ RECORD; CARRIAGE_CONTROL carriage_return; FORMAT fixed; SIZE 120;\ KEY 0; CHANGES no; PROLOG 3; SEG0_LENGTH 100; SEG0_POSITION 0; TYPE string;\ "; static int preprocess_html_source ( char *method, char *ident, char *source, int source_length ); static int load_file ( char *fname, char * lname, int *outlen, char **outbuf, int initial ); /****************************************************************************/ /* Main program/routine */ #ifndef CALLABLE_PREPROC #ifdef IS_MST static int preproc_main ( int argc, char **argv ) #else int main ( int argc, char **argv ) #endif { int status, i, j, k, length, s_size, used, sc, sg_size; char *source, *part_file, *part_name; char hdrline[64]; #ifdef IS_MST struct private_ctx *ctx; void *hfile, *link; char errmsg[256]; /* * Get pointer to thread's private data (particularly link). */ GET_SPECIFIC ( private_context, ctx ) if ( http_log_level > 6 ) tlog_putlog ( 7, "Context address: !XL, args: !AZ '!AZ' !AZ!/", ctx, argv[1], argv[2], argv[3] ); #else FILE *hfile; /* * Make connection back to server and set protocol version field for * the response to server. */ status = net_link_open(); if ( 0 == (status&1) ) exit ( status ); #endif /* * Validate method (argv[1]). We only understand GET and HEAD. */ if ( argc < 4 ) exit ( 20 ); if ( strncmp("GET",argv[1],4) && strncmp("HEAD",argv[1],5) ) { send_http_header ( "501 unsupported method", "text/plain" ); net_link_printf ("Unsupported method (%s)\n", argv[1] ); return 1; } if ( parse_part_name ( argv[2], &part_file, &part_name ) ) { /* * argv[2] was of form /path/filename.partname.type. */ status = extract_part ( part_file, part_name, &used, &source ); if ( status < 0 ) return status; argv[2] = part_file; } else { /* * Open file specified by argv[2] (fixed up and returned in part_file). */ #ifdef IS_MST ctx->last_mod = 0; #endif status = load_file ( part_file, part_file, &used, &source, 1 ); if ( status < 0 ) return status; } /* * Parse source. */ status = preprocess_html_source ( argv[1], argv[2], source, used ); return status; } #else /* * Set scriptlib mode to buffer all output. Call instead of cgi_begin_output * (called after cgi_init_env). Do not next CGI response header * (content-type) as this is assumed to be text/html. */ #include "scriptlib.h" static int old_nl_mode; static char *pp_method, *pp_ident; int cgi_begin_preprocessed ( char *method, char *ident ) { int status, old_mode; pp_method = malloc ( strlen(method)+1 ); strcpy ( pp_method, method ); pp_ident = malloc ( strlen(ident)+1 ); if ( !pp_ident ) return 0; strcpy ( pp_ident, ident ); old_nl_mode = net_link_set_mode ( 2 ); /* text mode, save ouput */ if ( (old_nl_mode&1) == 1 ) net_link_set_mode ( 3 ); return 1; } int cgi_end_preprocessed() { char *buffer; int status, length; /* * reset mode and get address of buffer holding saved data. */ net_link_set_mode ( old_nl_mode ); status = net_link_saved_output ( &buffer, &length ); if ( status&1 ) status = preprocess_html_source ( pp_method, pp_ident, buffer, length ); free ( pp_ident ); free ( pp_method ); return status; } #endif /* CALLABLE_PREPROC */ /****************************************************************************/ /* Recursive routine to scan source text for tags to pre-process. * Return value is number of segments. */ static int build_segment ( char *ident, char *source, int source_length, segptr *seglist, int *seglist_size, int seglist_start ) { int status, is_directive, i, j, k, length, s_size, sc, sg_size; segptr seg; char *targ[11], hdrline[128]; #ifdef IS_MST struct private_ctx *ctx; int reslen, tag_verify; /* * Get pointer to thread's private data and initialize local tag_verify. */ GET_SPECIFIC ( private_context, ctx ) tag_verify = ctx->tag_verify; #endif /* * Parse file, making list of individual blocks of text to send * back to server. */ seg = *seglist; sg_size = *seglist_size; /* make local copies of parameters */ sc = seglist_start; /* first element to load */ seg[sc].length = 0; seg[sc].addr = source; status = 1; for ( i = 0; i < source_length; i++ ) { char cur; cur = source[i]; if ( cur == '<' ) { /* * At beggining of HTML tag, go to state machine to parse. */ is_directive = parse_tag ( &source[i], source_length-i, &j, targ ); #ifdef DEBUG printf("Tag at %d (seg[%d].addr[%d]), length: %d, directive: %d verify: %d\n", i, sc, seg[sc].length, j, is_directive, tag_verify ); #endif if ( j > 0 && (!is_directive || tag_verify) ) { /* * Include parsed tag in output, as a comment the client * will ignore it. */ seg[sc].length += j; while ( seg[sc].length >= 1024 ) { /* Make current segment 1024 and put rest in new segment */ seg = alloc_segment ( seg, sc, &sg_size, &seg[sc].addr[1024] ); seg[sc+1].length = seg[sc].length - 1024; seg[sc++].length = 1024; } } i += j - 1; if ( is_directive ) { /* * Terminate current segment and make fresh one to hold * included info. Init start address of new segment to * point after tag. */ if ( seg[sc].length > 0 ) { seg = alloc_segment ( seg, sc++, &sg_size, &source[i] ); } else seg[sc].addr = &source[i]; j = 0; /* remove already added. */ status = parse_directive ( ident, targ, &seg[sc].length, &seg[sc].addr ); if ( status < 0 ) return status; #ifdef IS_MST tag_verify = ctx->tag_verify; #endif if ( status > 1 ) { /* recusive scan */ #ifdef IS_MST ctx->recur_level++; if ( ctx->recur_level < 5 ) sc = build_segment ( ident, seg[sc].addr, seg[sc].length, &seg, &sg_size, sc ); --ctx->recur_level; #else sc = build_segment ( ident, seg[sc].addr, seg[sc].length, &seg, &sg_size, sc ); #endif if ( sc < 0 ) return sc; } while ( seg[sc].length > 1024 ) { /* Make current segment 1024 and put rest in new segment */ seg = alloc_segment ( seg, sc, &sg_size, &seg[sc].addr[1024] ); seg[sc+1].length = seg[sc].length - 1024; seg[sc++].length = 1024; } if ( seg[sc].length > 0 ) { seg = alloc_segment ( seg, sc++, &sg_size, &source[i+1] ); } else { /* * result of directive was zero length, update start * point */ seg[sc].addr = &source[i+1]; } } } else { /* * Extend current segment to include this character. */ seg[sc].length++; if ( seg[sc].length >= 1024 ) { /* Segment at write limit, advance to next */ seg = alloc_segment ( seg, sc++, &sg_size, &source[i+1] ); } } } /* * Check for error status. */ if ( status < 0 ) return status; *seglist = seg; *seglist_size = sg_size; return sc; } /****************************************************************************/ /* Main engine for interpreting the HTML source for pre-processor directives. * When finished, network link is placed in CGI mode and result is dumped * to net_link. */ static int preprocess_html_source ( char *method, char *ident, char *source, int source_length ) { int status, is_directive, i, j, k, length, s_size, sc, sg_size; segptr seg; char *targ[11], hdrline[128]; #ifdef IS_MST struct private_ctx *ctx; int reslen, tag_verify; /* * Get pointer to thread's private data and initialize local tag_verify. */ GET_SPECIFIC ( private_context, ctx ) tag_verify = ctx->tag_verify; #else /* * Initialize global tag_verify. */ tag_verify = 0; #endif /* * Parse file, making list of individual blocks of text to send * back to server. */ sg_size = 1000; seg = (struct segment *) malloc ( sg_size * sizeof(struct segment) ); sc = build_segment ( ident, source, source_length, &seg, &sg_size, 0 ); if ( sc < 0 ) return -1; /* * Flush accumulated segments, compute total length and include in header. */ for (k = i = 0; i <= sc; i++) if (seg[i].length > 0) k += seg[i].length; #ifdef IS_MST /* 123456789012345678901234567 89012345678901234 */ tu_strcpy ( hdrline, "200 Sending Processed HTMLX\nContent-length: " ); tu_strint ( k, &hdrline[44] ); if ( !ctx->accessesKnown && ctx->last_mod ) { int hlen, cstatus, i, j; struct mstshr_envbuf env; char *if_modified_since; /* * Check for if-modified-since header. */ env.used = 1; env.prolog[0] = ctx->prolog[0]; env.prolog[1] = ctx->prolog[1]; env.prolog[2] = ctx->prolog[2]; env.prolog[3] = ctx->prolog[3]; cstatus = mstshr_cgi_symbols2 ( ctx->link, "", &env ); if_modified_since = mstshr_getenv ( "HTTP_IF_MODIFIED_SINCE", &env ); if ( http_log_level > 6 ) tlog_putlog ( 7, "if-modified header: !XL '!AZ'!/", if_modified_since, if_modified_since ? if_modified_since : "" ); if ( if_modified_since ) { unsigned int cache_time; for ( i = j = 0; if_modified_since[i]; i++ ) { if ( if_modified_since[i] != ' ' ) { if_modified_since[j++] = if_modified_since[i]; } } if_modified_since[j] = '\0'; cache_time = tf_decode_time ( if_modified_since ); if ( ctx->last_mod <= cache_time ) { tu_strcpy ( hdrline, "304 Cached copy good\nContent-length: " ); tu_strint ( k, &hdrline[37] ); method = "HEAD"; /* eliminate body */ } } /* Append last-modified header since counter not used */ hlen = tu_strlen ( hdrline ); tu_strcpy ( &hdrline[hlen], "\nLast-modified: " ); hlen += 16; tf_format_time ( ctx->last_mod, &hdrline[hlen] ); } #else sprintf ( hdrline, "200 Sending Processed HTMLX\nContent-length: %d", k ); #endif send_http_header ( hdrline, "text/html" ); if ( 0 == strncmp(method,"HEAD",5) ) return 1; for ( i = 0; i <= sc; i++ ) if ( seg[i].length > 0 ) { #ifdef IS_MST status = mst_write ( ctx->link, seg[i].addr, seg[i].length, &reslen ); #else status = net_link_write ( seg[i].addr, seg[i].length ); #endif if ( (status&1) == 0 ) break; } return 1; } /**************************************************************************/ /* Convert binary time to ascii string. If fmt_str does not start with '=', * use ctime format, otherwise use string after '=' as format for strftime(). */ static char *format_time (char *buffer, int bufsize, char *fmt, void *timebuf) { int i; #ifdef __DECC if ( fmt ) if ( *fmt == '=' ) { size_t size; LOCK_C_RTL size = strftime ( buffer, bufsize, &fmt[1], localtime((time_t *)timebuf) ); UNLOCK_C_RTL if ( (size > 0) && (size < bufsize) ) buffer[size] = '\0'; return buffer; } #endif /* * fallback to using ctime() routine. */ #ifdef IS_MST pthread_lock_global_np(); strncpy ( buffer, ctime((unsigned long *) timebuf), bufsize-1 ); buffer[bufsize-1] = '\0'; pthread_unlock_global_np(); #else strncpy ( buffer, ctime((unsigned long *) timebuf), bufsize-1 ); buffer[bufsize-1] = '\0'; #endif for ( i = 0; buffer[i]; i++ ) if ( buffer[i] == '\n' ) { buffer[i] = '\0'; break; } return buffer; } /**************************************************************************/ static segptr alloc_segment ( segptr seg, int sc, int *seg_size, char *start ) { sc = sc + 1; if ( sc >= *seg_size ) { *seg_size += 1000; seg = realloc ( seg, sizeof(struct segment)*(*seg_size) ); } seg[sc].length = 0; seg[sc].addr = start; return seg; } /**************************************************************************/ /* Prepare to send back response. Build standard response header. */ static int send_http_header ( char *stsline, char *content ) { int status; #ifdef IS_MST struct private_ctx *ctx; int reslen; /* * Get pointer to thread's private data (particularly link). */ GET_SPECIFIC ( private_context, ctx ); #endif /* * Enter CGI mode, set rundown to terminate mode on exit. */ #ifdef IS_MST status = mst_write ( ctx->link, "<DNETCGI>", 9, &reslen ); if ( (status&1) == 0 ) { abort_status = status; pthread_exit (&abort_status); } ctx->cgimode_active = 1; #else status = net_link_write ( "<DNETCGI>", 9 ); if ( 0 == (status&1) ) exit ( status ); status = net_link_set_rundown ( "</DNETCGI>" ); #endif /* * Send back standard header. */ if ( status&1 ) status = net_link_printf ( "Content-type:%s\nstatus: %s\n\n", content, stsline ); return status; } /*****************************************************************************/ static int extract_part ( char *fname, /* pathname of file being parsed */ char *partname, /* name of part to extract */ int *outlen, char **outbuf ) { char *ibuf, *tag, *name; int length, buf_used, buf_size; int state, tagstate, tagstart, isquoted, ibuf_i; char *partname_p; struct part_chunk { int start, end; } *parts; int current_part; int parts_size; #ifdef IS_MST void *ifile; char errmsg[256]; /* * Get pointer to private data. */ #else FILE *ifile; #endif *outlen = 0; /* * Open the file. */ length = strlen ( fname ); #ifdef IS_MST ifile = (length > 0) ? tf_open ( fname, "r", errmsg ) : (void *) 0; if ( ifile ) { /* Update last mod date in ctx if later than existing */ int size; unsigned uic, cdate, mdate; struct private_ctx *ctx; GET_SPECIFIC ( private_context, ctx ) tf_header_info ( ifile, &size, &uic, &cdate, &mdate ); if ( mdate > ctx->last_mod ) ctx->last_mod = mdate; } #else ifile = (length > 0) ? fopen ( fname, "r", "mbc=32" ) : (FILE *) 0; #endif if ( !ifile ) { send_http_header ( "500 Failed to open include file", "text/plain"); #ifdef IS_MST net_link_printf ( "Could not open include part file (%s)\n%s", fname, errmsg ); #else net_link_printf ( "Could not open include part file (%s)\n%s", fname, strerror ( errno, vaxc$errno ) ); #endif return -1; } buf_size = 10000; buf_used = 0; ibuf = malloc ( buf_size ); /* I make sure to allocate 8192 bytes, because that's the minimum memory page on an Alpha, and malloc() allocates full memory pages anyway... */ parts_size = 8192 / sizeof (struct part_chunk); parts = malloc ( parts_size * sizeof ( struct part_chunk ) ); current_part = 0; parts[current_part].start = -1; parts[current_part].end = 0; state = 0; tagstate = 10; /* 10 when looking for <!--#begin word-->, 20 when looking for <!--#end word--> */ isquoted = 0; ibuf_i = 0; #ifdef IS_MST while ( (length=tf_read(ifile,&ibuf[buf_used], buf_size-buf_used)) > 0) { #else while ( (length=fread(&ibuf[buf_used], 1, buf_size-buf_used, ifile)) > 0) { #endif if (parts[current_part].end == buf_used) parts[current_part].end += length; buf_used += length; if ( buf_used >= buf_size ) { buf_size += 10000; ibuf = realloc ( ibuf, buf_size ); } for (; state >= 0 && ibuf_i < buf_used; ibuf_i++) { char cur = ibuf[ibuf_i]; #ifdef DEBUG printf ("ibuf_i{1,2,3} = {%d, %d, %d}, ", parts[current_part].start, ibuf_i, parts[current_part].end); printf ("state = %d, ", state); printf ("tagstate = %d, tagstart = %d, ", tagstate, tagstart); printf ("cur = '%c' (0%%x%X), *partname_p = '%c' (0%%x%X)\n", (cur & 127) >= 32 ? cur : '.', cur, (*partname_p & 127 ) >= 32 ? *partname_p : '.', *partname_p); #endif switch (state) { case 0: if (cur == '<') { state = 1; tagstart = ibuf_i; } break; case 1: if (cur == '!') { state = 3; break; } state = 2; case 2: if (cur == '>') state = 0; break; case 3: if (cur != '-') { state = 2; ibuf_i--; break; } state = 4; break; case 4: if (cur != '-') { state = 2; ibuf_i--; break; } state = 5; break; case 5: if (cur != '#') { state = 2; ibuf_i--; break; } state = tagstate; break; case 6: /* Find terminator */ if (cur == '-') state = 7; else if (tagstate > 0 && !isspace(cur) && cur != '\n') state = 2; break; case 7: if (cur == '-') state = 8; else if (tagstate > 0) state = 2; else state = 6; break; case 8: if (isspace(cur) || cur == '\n') break; if (cur != '>') { tagstate = -tagstate; state = 6; break; } state = 0; #ifdef DEBUG { int saved_part = current_part; printf ("before: tagstate = %d, part = %d, start = %d, end = %d\n", tagstate, saved_part, parts[current_part].start, parts[current_part].end); #endif if (tagstate < 0) { /* restart from the beginning */ tagstate = -tagstate; if (tagstate == 20) parts[current_part].end = buf_used; } else if (tagstate == 10) { parts[current_part].start = tagstart; parts[current_part].end = buf_used; tagstate = 20; } else if (tagstate == 20) { parts[current_part++].end = ibuf_i + 1; if ( current_part >= parts_size ) { parts_size += 8192 / sizeof ( struct part_chunk ); parts = realloc ( parts, parts_size * sizeof ( struct part_chunk ) ); } /* -1 indicates it's not been set yet */ parts[current_part].start = -1; parts[current_part].end = buf_used; tagstate = 10; } #ifdef DEBUG printf ("after: tagstate = %d, part = %d, start = %d, end = %d\n", tagstate, saved_part, parts[current_part].start, parts[current_part].end); } #endif break; case 10: if (isspace(cur) || cur == '\n') break; if (cur != 'b' && cur != 'B') { state = 2; break; } state = 11; break; case 11: if (cur != 'e' && cur != 'E') { state = 2; break; } state = 12; break; case 12: if (cur != 'g' && cur != 'G') { state = 2; break; } state = 13; break; case 13: if (cur != 'i' && cur != 'I') { state = 2; break; } state = 14; break; case 14: if (cur != 'n' && cur != 'N') { state = 2; break; } state = 30; partname_p = partname; break; case 20: if (isspace(cur) || cur == '\n') break; if (cur != 'e' && cur != 'E') { state = 2; break; } state = 21; break; case 21: if (cur != 'n' && cur != 'N') { state = 2; break; } state = 22; break; case 22: if (cur != 'd' && cur != 'D') { state = 2; break; } state = 30; partname_p = partname; break; case 30: if (isspace(cur) || cur == '\n') break; partname_p = partname; state = 31; isquoted = 0; if (cur == '"') { isquoted = 1; break; } case 31: if (*partname_p == cur) { partname_p++; break; } if ( (*partname_p == '\0') ) { if ( isquoted ) { if ( cur == '"' ) { state = 33; break; } } else { if ( (cur == '-') || isspace(cur) || (cur == '\n') ) { if ( cur == '-' ) ibuf_i--; state = 33; break; } } } if ( cur == '-' ) { tagstate = -tagstate; ibuf_i--; state = 6; break; } partname_p = partname; /* restart search */ if ( isspace(cur) || (cur == '\n') ) state = 30; else state = 32; break; case 32: /* remainder of rejected label label */ if ( isspace(cur) || (cur == '\n') ) state = 30; if ( cur == '-' ) { ibuf_i--; tagstate = (-tagstate); state = 6; } break; case 33: if ( cur == '-' || cur == '>' ) { state = 6; ibuf_i--; } break; } } if (tagstate == 20 && state == -1) break; } /* * rundown file. */ #ifdef IS_MST tf_close ( ifile ); #else fclose(ifile); #endif { int i, tmp; for (i = 0, ibuf_i = 0; i <= current_part; i++) if (parts[i].start >= 0) { /* * Copy contents. */ strncpy(ibuf + ibuf_i, &ibuf[parts[i].start], tmp = parts[i].end - parts[i].start); ibuf_i += tmp; } if (ibuf_i) buf_used = ibuf_i; } ibuf[buf_used] = '\0'; *outbuf = ibuf; *outlen = buf_used; return *outlen; } #ifdef IS_MST /*****************************************************************************/ /* Read named file into memory. Return -1 on error, 2 on success. */ static int load_file ( char *fname, char *lname, int *outlen, char **outbuf, int initial ) { char *ibuf; int length, buf_used, buf_size, cache_allowed, cache_status; struct private_ctx *ctx; void *ifile; struct tfc_context tfc; char errmsg[256]; /* * Get pointer to private data. */ GET_SPECIFIC ( private_context, ctx ) /* * cache status: 0-nocache, 1-reading from cache, 2 writing cache. */ cache_status = tfc.mode = 0; if ( *fname && ctx->cache_allowed ) { cache_status = tfc_cached_open ( 1, fname, &ifile, errmsg, &tfc ); #ifdef DEBUG printf("Cache lookup on %s, status: %d\n", fname, cache_status ); #endif } else ifile = *fname ? tf_open ( fname, "r", errmsg ) : (void *) 0; if ( ifile ) { /* Update last mod date in ctx */ int size; unsigned uic, cdate, mdate; tf_header_info ( ifile, &size, &uic, &cdate, &mdate ); if ( mdate > ctx->last_mod ) ctx->last_mod = mdate; } else if ( cache_status == 1 ) { /* Update last mod date from tfc header */ if ( tfc.hdr.mdate > ctx->last_mod ) ctx->last_mod = tfc.hdr.mdate; } if ( !ifile && (cache_status != 1) ) { send_http_header ( initial ? "500 Failed to open file" : "500 Failed to open include file", "text/plain"); net_link_printf ( initial ? "Could not open file for '%s' HTML pre-processing\n%s" : "Could not open include file (%s)\n%s", lname, errmsg ); return -1; } buf_used = 0; if ( ifile ) { /* * Read file contents. */ buf_size = initial ? 20000 : 10000; ibuf = malloc ( buf_size ); while ( ibuf && (length=tf_read(ifile,&ibuf[buf_used], buf_size-buf_used)) > 0) { buf_used += length; if ( buf_used >= buf_size ) { buf_size += (initial ? 20000 : 10000); ibuf = realloc ( ibuf, buf_size ); } } tf_close ( ifile ); if ( cache_status == 2 && ibuf ) { /* * Save contents in cache in 4000 byte chunks. */ int i, rec_size;; for ( i = 0; i < buf_used; i += rec_size ) { rec_size = buf_used-i; if ( rec_size > 4000 ) rec_size = 4000; if ( 1 != tfc_put ( &tfc, &ibuf[i], rec_size ) ) break; } } } else if ( cache_status == 1 ) { /* * copy cache contents. */ buf_size = tfc.hdr.size; ibuf = malloc ( buf_size ); for ( buf_used = 0; ibuf && (buf_used < buf_size); buf_used+=length) { void *rec; if ( 1 != tfc_get ( &tfc, &rec, &length ) ) break; memcpy ( &ibuf[buf_used], rec, length ); } } /* * Delete cache entry on error (null ibuf) */ if ( tfc.mode ) tfc_rundown_context ( &tfc, ibuf ? 0 : 1 ); *outbuf = ibuf; *outlen = buf_used; if ( !ibuf ) { send_http_header ( initial ? "500 Failed to load file" : "500 Failed to load include file", "text/plain"); net_link_printf ( "Memory allocation failure while loading %s", lname); return -1; } return 2; } #else /* no MST */ /*****************************************************************************/ /* Read named file into memory. Return -1 on error, 2 on success. */ static int load_file ( char *fname, char *lname, int *outlen, char **outbuf, int initial ) { char *ibuf; int length, buf_used, buf_size; FILE *ifile; ifile = *fname ? fopen ( fname, "r", "mbc=32" ) : (FILE *) 0; if ( !ifile ) { send_http_header ( initial ? "500 Failed to open file" : "500 Failed to open include file", "text/plain"); net_link_printf ( initial ? "Could not open file for '%s' HTML pre-processing\n%s" : "Could not open include file (%s)\n%s", lname, strerror ( errno, vaxc$errno ) ); return -1; } buf_used = 0; buf_size = initial ? 20000 : 10000; ibuf = malloc ( buf_size ); while ( ibuf && (length=fread(&ibuf[buf_used], 1, buf_size-buf_used, ifile)) > 0) { buf_used += length; if ( buf_used >= buf_size ) { buf_size += (initial ? 20000 : 10000); ibuf = realloc ( ibuf, buf_size ); } } fclose(ifile); *outbuf = ibuf; *outlen = buf_used; if ( !ibuf ) { send_http_header ( initial ? "500 Failed to load file" : "500 Failed to load include file", "text/plain"); net_link_printf ( "Memory allocation failure while loading %s", lname); return -1; } return 2; } #endif /* MST */ /*****************************************************************************/ /* Handle the file-related server directives (include, fsize, flastmod). */ static int do_include ( int opcode, /* 1 - include, 2-fsize, 3-flastmod */ char *path, /* pathname of file being parsed */ char **drctv, /* Parsed directive tokens 1=tag, 2=fname */ int *outlen, char **outbuf ) { char *ibuf, *tag, *name, fname[600]; int length, buf_used, buf_size; int have_part; #ifdef IS_MST struct private_ctx *ctx; void *ifile; /* * Get pointer to private data. */ GET_SPECIFIC ( private_context, ctx ) #else FILE *ifile; #endif /* * Examine field1 argument to determine include type. */ tag = drctv[1]; name = drctv[2]; *outlen = 0; have_part = 0; if ( opcode == 1 && 0 == strncmp ( drctv[3], "PART", 5 ) ) { have_part = 1; #ifdef DEBUG printf("Part name is %s\n", fname); #endif } if ( 0 == strncmp ( tag, "FILE", 5 ) ) { /* * Construct filename relative to current path. */ strncpy ( fname, path, 255 ); fname[255] = '\0'; for ( length = strlen(fname); (length > 0) && (fname[length] != '/'); --length ); length++; strcpy ( &fname[length], name ); length = strlen ( fname ); if ( length > 0 ) if ( fname[length-1] == '"' ) fname[length-1] = '\0'; } else if ( 0 == strncmp ( tag, "VIRTUAL", 8 ) ) { /* * Have server translate name. */ int status; #ifdef IS_MST mst_write ( ctx->link, "<DNETXLATEV>", 12, &length ); #else net_link_write ( "<DNETXLATEV>", 12 ); #endif status = net_link_query ( name, fname, sizeof(fname)-1, &length); if ( 0 == (status&1) ) length = 0; fname[length] = '\0'; } /* * Take action based upon opcode. */ if ( opcode == 1 && !have_part ) { /* * Include the file. */ int status; status = load_file ( fname, name, outlen, outbuf, 0 ); if ( status < 0 ) return status; return status; } else if ( opcode == 1 && have_part ) { /* * Scan file and return named part. */ length = extract_part ( fname, drctv[4], outlen, outbuf ); return 2; } else { /* * fsize(2) and flastmod(3). Load stat structure. */ stat_t info; int status; LOCK_C_RTL status = (length > 0) ? stat ( fname, &info ) : -1; UNLOCK_C_RTL if ( status != 0 ) { send_http_header ( "500 Failed to access file", "text/plain"); net_link_printf ( "Could not read file attributes (%s)\n", name ); return -1; } /* * Format information. */ *outbuf = malloc ( 64 ); if ( opcode == 2 ) { sprintf ( *outbuf, "%d", info.st_size ); } else { /* * Check if user included FMT tag. */ char fmt[100]; fmt[0] = '\0'; if ( strncmp(drctv[3],"FMT",4) == 0 ) { fmt[0] = '='; strncpy ( &fmt[1], drctv[4], sizeof(fmt)-2 ); fmt[99] = '\0'; } format_time ( *outbuf, 64, fmt, &info.st_mtime ); } *outlen = strlen ( *outbuf ); } return 1; } /*****************************************************************************/ /* Return accesses count for indicated path, updating count by 1 on first call * A version number less than or equal to 0 means ignore the version number. */ static int get_access_count ( char *path, int version ) { #ifdef IS_MST IFILE *fp; struct private_ctx *ctx; #define accesses_known ctx->accessesKnown #define accesses ctx->Accesses #define rec_version ctx->version #else IFILE *fp; static int accesses_known = 0, accesses, rec_version; #endif char accessesRecord[128]; char accessesPath[104]; char accessesStr[20]; int status, i, new_rec; size_t length; stat_t info; #ifdef IS_MST /* * Get private data. */ GET_SPECIFIC ( private_context, ctx ) #endif /* * Reset accesses_known if version changes. */ if ( accesses_known ) if ( (version > 0) && (version != rec_version) ) { accesses_known = 0; } if ( !accesses_known ) { /* * Get/update access count/version in file. Try to open file. */ #ifdef IS_MST iacquire_db(); /* serialize access */ ctx->have_lock = 1; fp = count_db ? count_db : ifopen ( "www_root:[000000]accesses.dat","r+" ); #else fp = ifopen ( "www_root:[000000]accesses.dat","r+" ); #endif if ( !fp ) { /* * See if we have permission to write new records. */ LOCK_C_RTL status = stat ( path, &info ); UNLOCK_C_RTL if ( status == 0 ) status = icheck_access ( "www_root:[000000]accesses.permissions", info.st_uid ); if ( (status&1) == 0 ) { send_http_header ( "500 Failed to create file", "text/plain"); net_link_printf ( "No permission to create accesses.dat file, code %d\n", status ); return -1; } if ( (status&1) == 0 ) { } /* * Attempt to create indexed file and retry open. */ status = ifdlcreate ( access_file_fdl, "accesses.dat", "www_root:[000000]" ); if ( status & 1 ) fp = ifopen ( "www_root:[000000]accesses.dat","r+" ); } if ( !fp ) { send_http_header ( "500 Failed to access file", "text/plain" ); net_link_printf ( "Could not open accesses.dat file."); return -1; } /* * File is now open, do indexed read to get record for specified path. */ #ifdef IS_MST count_db = fp; /* share amongst several threads */ tu_strnzcpy ( accessesPath, path, 100 ); tu_strupcase ( accessesPath, accessesPath ); i = tu_strlen ( accessesPath ); #else for ( i = 0; (i < 100) && path[i]; i++ ) accessesPath[i] = _toupper ( path[i] ); #endif while ( i < 100 ) accessesPath[i++] = ' '; /* pad to 100 bytes */ accessesPath[i] = '\0'; status = ifread_rec(accessesRecord,120,&length,fp,0,accessesPath,100); if ( status & 1 ) { accessesRecord[120] = '\0'; sscanf(accessesRecord+100,"%10d%10d",&accesses, &rec_version); new_rec = 0; } else { /* * Read error on record, assume non-existent and make initial. */ accesses = 0; rec_version = 0; new_rec = 1; /* * Check if file owner has permission to add to accesses.dat file. */ LOCK_C_RTL status = stat ( path, &info ); UNLOCK_C_RTL if ( status == 0 ) status = icheck_access ( "www_root:[000000]accesses.permissions", info.st_uid ); if ( (status&1) == 0 ) { send_http_header ( "500 Failed to extend file", "text/plain"); net_link_printf ( "No permission to add records to accesses.dat, code %d\n", status ); return -1; } } /* * Reset or update access count by 1 and update record in file. */ if ( version <= 0 ) version = rec_version; else if ( version != rec_version ) accesses = 0; accesses++; sprintf(accessesRecord,"%-100.100s%10.10d%10.10d",accessesPath,accesses, version); if ( new_rec ) { #ifndef IS_MST iferror(fp); #endif status = ifwrite_rec(accessesRecord,120,fp); } else { status = ifupdate_rec(accessesRecord,120,fp); } #ifdef IS_MST ctx->have_lock = 0; irelease_db(); #else if ( (status&1) == 0 ) iferror(fp); ifclose(fp); #endif accesses_known = 1; } return accesses; } #ifdef IS_MST #undef accesses #undef accesses_known #undef rec_version #endif /*****************************************************************************/ /* Lookup indicate variables and return their values. */ static int do_echo ( char *path, /* pathname of file being parsed */ char **drctv, /* Elements of directive */ int *outlen, char **outbuf ) { int SYS$ASCTIM(), timlen, i, j, length; char *timestr, *tag, *name; static enum vcodes { C_DATE_LOCAL, C_DOCUMENT_NAME, C_LAST_MODIFIED, C_ACCESSES, C_ACCESSES_ORDINAL, C_VMS_VERSION, C_HW_NAME, C_SERVER_ACCOUNT, C_SERVER_NAME, C_SERVER_VERSION, C_GETENV, C_FINIS }; enum vcodes vcode; static struct { enum vcodes code; char delimiter; int min_len; char *keyword; } vtbl[] = { { C_DATE_LOCAL, '=', 10, "DATE_LOCAL" }, { C_DOCUMENT_NAME, '\0', 13, "DOCUMENT_NAME" }, { C_LAST_MODIFIED, '=', 13, "LAST_MODIFIED" }, { C_ACCESSES, ';', 8, "ACCESSES" }, { C_ACCESSES_ORDINAL, ';', 16,"ACCESSES_ORDINAL" }, { C_SERVER_NAME, '\0', 11, "SERVER_NAME" }, { C_SERVER_VERSION, '\0', 14, "SERVER_VERSION" }, { C_GETENV, '=', 6, "GETENV" }, { C_VMS_VERSION, '\0', 11, "VMS_VERSION" }, { C_HW_NAME, '\0', 7, "HW_NAME" }, { C_FINIS, '\0', 0, "" } }; static char hardware[31], os_version[12]; static $DESCRIPTOR(vms_version_dx, os_version); static $DESCRIPTOR(hardware_dx,hardware); tag = drctv[1]; name = drctv[2]; *outlen = 0; if ( strncmp ( tag, "VAR",4 ) ) return 0; /* syntax error */ /* * Lookup var keyword in table, putting it's code number in vcode. */ length = strlen ( name ); for ( vcode = C_FINIS, i = 0; vtbl[i].code != C_FINIS; i++ ) { if ( length >= vtbl[i].min_len ) { j = vtbl[i].min_len; if ( 0 == strncmp ( name,vtbl[i].keyword,j) ) { if ( name[j] == '\0' || name[j] == vtbl[i].delimiter ) { vcode = vtbl[i].code; break; } } } } /* * Process based upon vcode. */ switch ( vcode ) { stat_t info; int status, code, accesses, version, last, last2; char *env_val; case C_DATE_LOCAL: /* * See if format info was supplied. */ if ( name[10] == '=' ) { time_t now; *outbuf = malloc ( 64 ); now = time(NULL); format_time ( *outbuf, 64, &name[10], &now ); *outlen = strlen ( *outbuf ); } else { struct { long l; char * a; } timebuf; *outbuf = malloc ( 24 ); timebuf.l = 24; timebuf.a = *outbuf; SYS$ASCTIM ( outlen, &timebuf, 0, 0 ); } break; case C_DOCUMENT_NAME: /* Echo filename of document (part to right of last /) */ *outbuf = strrchr(path,'/'); if ( *outbuf ) *outbuf = *outbuf+1; else *outbuf = path; *outlen = strlen ( *outbuf ); break; case C_LAST_MODIFIED: /* * Echo last modified date of current file path. */ LOCK_C_RTL status = stat ( path, &info ); UNLOCK_C_RTL if ( status != 0 ) { send_http_header ( "500 Failed to access file", "text/plain"); net_link_printf ( "Could not read file attributes (%s)\n", path ); return -1; } /* * Format information. */ *outbuf = malloc ( 64 ); format_time ( *outbuf, 64, &name[13], &info.st_mtime ); *outlen = strlen ( *outbuf ); break; case C_ACCESSES: case C_ACCESSES_ORDINAL: /* * Echo access count. */ /* * see if name includes version number. */ version = 0; if ( (vcode == C_ACCESSES) && (name[8] == ';') ) version = atoi(&name[9]); else if ( (vcode == C_ACCESSES_ORDINAL) && (name[16] == ';') ) version = atoi(&name[17]); accesses = get_access_count ( path, version ); if ( accesses < 0 ) return accesses; *outbuf = malloc ( 24 ); if ( accesses < 1000 ) sprintf(*outbuf, "%d",accesses ); else if ( accesses < 1000000 ) sprintf(*outbuf, "%d,%03.3d", accesses/1000, accesses%1000 ); else sprintf ( *outbuf, "%d,%03.3d,%03.3d", accesses/1000000, accesses%1000000/1000, accesses%1000 ); if ( vcode == C_ACCESSES_ORDINAL ) { /* tack on suffix */ static char *ordination[10] = { "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th" }; char *suffix; last2 = accesses %100; suffix = &(*outbuf)[strlen(*outbuf)]; last = accesses % 10; if ( (last2 >= 11) && (last2 <= 13) ) last = 0; strcpy ( suffix, ordination[last] ); } *outlen = strlen ( *outbuf ); break; case C_SERVER_NAME: *outbuf = malloc ( 256 ); status = net_link_query ( "<DNETHOST>", *outbuf, 255, outlen ); if ( (status&1) == 0 ) *outlen = 0; break; case C_SERVER_VERSION: *outbuf = malloc ( 256 ); status = net_link_query ( "<DNETID2>", *outbuf, 255, outlen ); if ( (status&1) == 0 ) *outlen = 0; else *outlen = strchr(*outbuf, ' ') - *outbuf; break; case C_GETENV: /* * Return value of arbitrary logical or DCL symbol. */ if ( length < 8 ) break; /* invalid syntax */ #ifdef IS_MST pthread_lock_global_np(); #endif env_val = getenv ( &name[7] ); if ( env_val ) { *outlen = strlen ( env_val ); *outbuf = malloc ( *outlen + 1 ); strcpy ( *outbuf, env_val ); } else { *outbuf = "unknown"; *outlen = 7; } #ifdef IS_MST pthread_unlock_global_np(); #endif break; case C_VMS_VERSION: code = SYI$_VERSION; #ifdef IS_MST *outbuf = malloc ( sizeof(os_version)+1 ); pthread_lock_global_np(); LIB$GETSYI ( &code, 0, &vms_version_dx, outlen, 0, 0 ); tu_strnzcpy ( *outbuf, vms_version_dx.dsc$a_pointer, *outlen ); pthread_unlock_global_np(); #else *outbuf = os_version; LIB$GETSYI ( &code, 0, &vms_version_dx, outlen, 0, 0 ); if ( *outlen < sizeof(os_version) ) os_version[*outlen] = '\0'; #endif break; case C_HW_NAME: code = SYI$_HW_NAME; #ifdef IS_MST *outbuf = malloc ( sizeof(hardware)+1 ); pthread_lock_global_np(); LIB$GETSYI ( &code, 0, &hardware_dx, outlen, 0, 0 ); tu_strnzcpy ( *outbuf, hardware_dx.dsc$a_pointer, *outlen ); pthread_unlock_global_np(); #else *outbuf = hardware; LIB$GETSYI ( &code, 0, &hardware_dx, outlen, 0, 0 ); if ( *outlen < sizeof(hardware) ) hardware[*outlen] = '\0'; #endif break; default: /* * Unknown var. */ *outbuf = malloc ( length + 3 ); sprintf ( *outbuf, "?%s?", name ); *outlen = length + 2; break; } return 1; } /*****************************************************************************/ /* Handle config directive. * Tags: * verify=[0|1] */ static int do_config ( char *path, char *field1, char *field2, int *outlen, char **outbuf ) { if ( strncmp ( field1, "VERIFY", 7) == 0 ) { /* * Set tag verify global that governs whether tag will be included. */ #ifdef IS_MST struct private_ctx *ctx; GET_SPECIFIC ( private_context, ctx ) if ( *field2 == '1' || *field2 == 'Y' ) ctx->tag_verify = 1; else ctx->tag_verify = 0; #else if ( *field2 == '1' || *field2 == 'Y' ) tag_verify = 1; else tag_verify = 0; #endif } *outlen = 0; *outbuf=" "; return 0; } /*****************************************************************************/ /* Top routine to handle interpreting an server-side html directive, * optionally returning to the caller a pointer to buffer containing * additional data to send to the client (inserted at the point of * the directive). A zero outlen indicates no optional data is returned. * * The directive takes the form <!--#command [tag1=value [tag2=value]]--> * * Note that tag array is modified by this routine. * Note also that output buffers must be statically allocated, they are not * read by the caller until final processing. * * If this routine generates an error response, it must return -1 as the * function value. */ static int parse_directive ( char *path, /* Pathname of file being parsed */ char **drctv, /* 5 elements of parsed directive */ int *outlen, /* Size of result output buffer */ char **outbuf ) /* Address of result buffer */ { int i, taglen; char *command; /* * Scan the directive tag and parse into command and tags. */ #ifdef DEBUG printf("Directive: '%s' '%s'='%s' '%s'='%s'\n\n", drctv[0], drctv[1], drctv[2],drctv[3],drctv[4] ); #endif command = drctv[0]; /* * Now interpret command. */ if ( 0 == strncmp(command,"INCLUDE",8) ) { /* * Include file. */ return do_include ( 1, path, drctv, outlen, outbuf ); } else if ( 0 == strncmp ( command, "ECHO",5 ) ) { /* * Echo will insert values of special variables into stream. */ return do_echo ( path, drctv, outlen, outbuf ); } else if ( 0 == strncmp(command,"FSIZE",6) ) { /* * File attributes. */ return do_include ( 2, path, drctv, outlen, outbuf ); } else if ( 0 == strncmp(command,"FLASTMOD",9) ) { return do_include ( 3, path, drctv, outlen, outbuf ); } else if ( 0 == strncmp ( command, "CONFIG",7 ) ) { /* * Set configuration parameters. */ return do_config ( path, drctv[1], drctv[2], outlen, outbuf ); } else if ( 0 == strncmp ( command, "BEGIN", 6 ) || 0 == strncmp ( command, "END", 4 ) ) { /* For some reason, I HAVE to put one space in the output buffer. If I just do '*outlen = 0;', the ending ">" will be preserved in the .htmlx file. If I do '*outbuf = ""; *outlen = 0;', everything after on of these directives will be ignored... -- Richard Levitte */ *outbuf = " "; *outlen = 0; /* fixed bug, so can use zero length, DLJ */ return 1; } /* * Invalid directive in file. */ send_http_header ( "500 Bad directive in file", "text/plain" ); net_link_printf("invalid directive: (%s %s %s)\n", command, drctv[1], drctv[2] ); *outlen = 0; return -1; } /*****************************************************************************/ /* Parse HTML tag. If HTML tag looks like a pre-processor directive, * fill in targ array with parsed elments. (command, (tag,value) pairs). * * Return value: * 0 Tag is NOT a pro */ static int parse_tag ( char *tag, /* start of tag */ int maxlen, /* remaining bytes in input file */ int *taglen, /* Size of tag, including closing '>'*/ char **targ ) /* Elements, cmd and up to 5 tag/value pairs */ { int state, i, j, eot_ok; char *directive, cur; /* * First, determine if tag if directive or not, Size of tag is * also determined. */ if ( *tag != '<' ) { *taglen = 0; return 0; } for ( state = i = 0; (state >= 0) && (i < maxlen); i++ ) { cur = tag[i]; switch (state) { case 0: if ( cur != '<' ) { *taglen = i+1; return 0; } state = 1; break; case 1: if ( cur == '!' ) { state = 3; break; } state = 2; case 2: /* Not a directive, search for end. */ if ( cur == '>' ) { *taglen = i+1; return 0; } break; case 3: if ( cur != '-' ) { state = 2; --i; break; } state = 4; break; case 4: if ( cur != '-' ) { state = 2; --i; break; } state = 5; break; case 5: if ( cur != '#' ) { state = 2; --i; break; } state = 6; break; case 6: /* Find terminator */ if ( cur == '-' ) state = 7; break; case 7: if ( cur == '-' ) state = 8; else state = 6; break; case 8: if ( cur == '>' ) state = -1; else state = (cur=='-') ? 8 : 6; break; } } /* * Getting to this point means we parsed a directive (state = -1). * Make copy of tag and parse the copy (portions of copy will be upcased). */ directive = malloc ( i + 1 ); strncpy ( directive, tag, i ); directive[i] = '\0'; tag = directive; #ifdef DEBUG printf("comment: '%s' l = %d\n", directive, i ); #endif /* * Initialize return array to all null strings. */ *taglen = i; targ[0] = &tag[5]; for ( i = 1; i < 11; i++ ) targ[i] = ""; eot_ok = 1; /* * Parse tag into directive components using state machine. */ for ( state = j = 0, i = 5; (state >=0) && (i < *taglen-3); i++ ) { #ifdef DEBUG printf("state = %d, tag[%d] = '%c'\n", state, i, tag[i] ); #endif cur = tag[i]; switch ( state ) { case 0: if ( isspace(cur) || (cur=='\n') ) { /* found end of targ[0] */ tag[i] = '\0'; #ifdef IS_MST tu_strupcase ( tag, tag ); #endif state = 1; j = 1; targ[j] = &tag[i]; } #ifndef IS_MST else tag[i] = _toupper(cur); #endif break; case 1: /* look for start of targ[j] */ if ( !isspace(cur) /* || (cur == '\n') */ ) { targ[j] = &tag[i]; state = 2; eot_ok = 0; } else break; case 2: /* look for end of targ[1] */ if ( cur == '=' ) { tag[i] = '\0'; state = 3; #ifdef IS_MST tu_strupcase ( targ[j], targ[j] ); #endif } else if ( isspace(cur) ) state = -1; /* syntax error */ #ifndef IS_MST else tag[i] = _toupper(cur); #endif break; case 3: /* Check for proper syntax */ if ( cur == '"' ) { j++; /* Advance to value part */ targ[j] = &tag[i+1]; state = 4; } else state = -1; break; case 4: if ( cur == '"' ) { /* end of targ[2] */ tag[i] = '\0'; j++; targ[j] = &tag[i]; state = 1; /* look for next value */ eot_ok = 1; } break; } } if ( state == -1 ) { free ( directive ); return 0; /* improper syntax */ } return 1; } /*****************************************************************************/ /* * Scan file specification to see if the filename contains an embedded * part name. If a partname is present, copy the partname to a separate * string and reconstruct filename without the embedded partname. * * Function value returned is 1 if partname present and 0 if not. */ static int parse_part_name ( char *fname, char **pfile, char **pname ) { int length, i, dot1, dot2; char cur, *tail; /* * Parse from back, looking for postiions in fname of 1st 2 dots after * the last slash. */ dot1 = dot2 = -1; length = strlen ( fname ); for ( i = length - 1; i >= 0; --i ) { cur = fname[i]; if ( (cur == '/') || (cur == '>') || (cur == ']') ) break; if ( cur == '.' ) { dot2 = dot1; dot1 = i; } } *pfile = fname; if ( dot2 < 0 ) return 0; /* normal filename */ if ( fname[dot2+1] >= '0' && fname[dot2+1] <= '9' ) return 0; /* * Reconstruct filename without part and put part in separate. */ *pname = malloc ( dot2 - dot1 ); strncpy ( *pname, &fname[dot1+1], (dot2-dot1-1) ); (*pname)[dot2-dot1-1] = '\0'; *pfile = malloc ( length +1 ); strncpy ( *pfile, fname, dot1 ); /* all before 1st dot */ strcpy ( (*pfile)+dot1, &fname[dot2] ); /* rest */ if ( (dot1+1) == dot2 ) return 0; /* part name null */ return 1; }