diff --git a/js/oafp.js b/js/oafp.js index 7da943f6e..03f675ce3 100755 --- a/js/oafp.js +++ b/js/oafp.js @@ -1,10 +1,10 @@ var params=processExpr(" "); -// (author: Nuno Aguiar, version: 20260117, license: Apache 2.0, url: https://github.com/openaf/oafp) +// (author: Nuno Aguiar, version: 20260309, license: Apache 2.0, url: https://github.com/openaf/oafp) // --- const _oafphelp={'docs/EXAMPLES.md':af.fromBytes2String(io.gunzip(af.fromBase64("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"))), -'docs/TEMPLATE.md':af.fromBytes2String(io.gunzip(af.fromBase64("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"))), +'docs/TEMPLATE.md':af.fromBytes2String(io.gunzip(af.fromBase64("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"))), 'docs/USAGE.md':af.fromBytes2String(io.gunzip(af.fromBase64("H4sIAAAAAAAA/9V923IbSZbYO7+ijB5HgVoQbFHdHb2Y5XgokmqxmxQ5BDTdY42CKlQVgJIKVVBlgZduzav3cR1hP23Y4Ue/+AP85I/xD9if4HPLS11AEmpsx1oPIqoq8+Tt5Mlzzy+880WcHbzwFkUexkrlxZYH/7aePHmtgmn85MnAu8qDycJ7M0nS+K33Jl+USZ6pt1dbW6PgQ6y8IPOSbLEse95SLYM0vfMCLwrKwFNlsQzLZRF7ahnOvEB571We9aBC5JVFkKlJXsyVl5RemSOU+OMyuQ7SOCvr9ZMMvuflLC48rBOUXl541wk2l/wcYH/63mgWe/myhI5w7RAgjmMPel3GRQwtzop8OZ15358dDy+Cctbzhn86RTg8fl952WmSfaTewVxcJ1HsdDMuFEEMUpUj2GCxSBMskEP/+1tbf/BOJjDuK5ykK4RKv/ZVPo9rr35OFvh3MLDfEmg7L51WZzFPqXeTpCk2F98u4rDk9uDRKcmDUmWUZLuLZBH3oSvnvEReUKyuCatxxUu5D3O+jK/63uEsDj9Q42miSgXF0/wGJxxGndL74DpI0mCcwkxzE9jaaxV773au3+EQ3yXZfuffdd7RLL6D5eAnaDtKVJhfwwKq5WKRF9gjGqLqubiA1XgRAfTWF194ZwGsvTQ22Nr6JGPzPnlHsQqLRD9tfdqRf+YHP0GVnRliNBQazmA85QymO8rD5RwRDb/P4nRB3w8QVTLAp+vYuwnusNuqWQWGSaiovG7cn/YFw3Ac8XyRBmW8TVBxaQkqIiY9ALhFUMBsdZNJdcFp+RANliqOuHo4j7xGpwACQcJp4jrwJr6Nw2UJL70wn8/xE/frw3IMK5/2sOcf4mIby07jkhaSoIR5VsKAFLVHe2ZFez1pDGFjv3Ag3HWuxriKUGDtPDNo2Y2yY7tRPAmWaTnwwrKIZZIAqC3PYMq7RcsMJbIXyuJOMDlYlvlOFJeE3QJtUuRzHEKmiRpvadgDBZA2xBQeEYxQWqP+U131McWqRBUeU4Ha8lpaWwBxaYcgE4K1sTWuvbpBp3hOQLG0Jl/3VjGdhGbkyyfvOMOdq3j98yJMsqmXT+gReyPlCEMLr0tEQQ1ommH9E0BtwIMgug6yUM+3gV4yZOrf8PzV7vD0/BVQnwL2EODkIgesxqZ8Kud7WTBnHPZxWD4hcQAnBH9W3H1eZdwQsPIwXumnHg40T3PiPX5WaFupa553INeIafvwokewD4d/1oQGq0LDRFaw8of4jmsACieZEOh5sNhFOqmha1IN7U+SW7fXBokJDoOkDViBiTS2BuqmSErYodidChyubAHBYRTDbBI4Hw5NmGIhpHTi4BsCUAPP1XBpshXwARmgW3HaAM17QUi5suWEiUDUqu1Y+eKcbbD3TT04HYp4GhQR1aTPeRGZdcbFqPShApk+G2KGdKpKwq5SbHinuGIMyFOg4E1ouCVi/hrrQwgoinPmYe1lIfv2Mi6LJL6OBVuJGlQm8fXlqa4xj8tZrtcHnn0k9aYgIwDPzKwsF54UFySELkTJZAI8DJw91Mx3xyNGTQCFMzhXq0DHt3C20iwDTD6xLI8F8A3r8zvYNOW25pks1c2c/SyDYdrS3l5gi++W0HiFEzOlqO9JpuDYKmQTEIrSMiTTDBbAGw5Pd0enQy8E4pJMkhCOVQ/6nUQBH/i4kLM6xYEtiadzk/AEhkSHsyDLAOF8pEF+DzAAiIBsF9r8AXwTMuBTM2meLzTF0HuVekofZMk10o/v6E22nI+R7kw8GGSeRc4UaZBhqhq7iiAiqyHN6C0UwhYvmACGcHRmXhdGiZt0G7cukU6qGt6FgqZpMkbwh8gQALg0mSfIcTG9muDkl7jxIosB+UUQfiDKBx1WCe494i1KpCwoAPiqybAFwmdaro0XFxnR6trs/uXg7JQWCPnJApde4fYMiexNqAU8ZWCrFYz5OJ3LLLklYIC5sBcmy5Sp6s6ED6CdiXuuWa4kqDdMFI2qalaWGnR2BvDDAfWP4Ma3AR54OJwfcQWCkIgXiTpIW4CNGgfKmVECZ2rRYuIXaGlOEOsAFaxo2DweboDqIQr7iPDTvLgDMWE51g8+bkgmoI/oBDYLDLdiBrmFt8W9MI0dioxiGM2BObrKPE9h4Q0YnJtrRCyqqtapm2STnDnNeLycOtt+USRIt+n1FFd9DlMdTGkEIF68kDNcyzQgeN3peZjkKYgoxMjgaQGiKvIEVz0QtYBQXtE6XAHTcEUS2guUwaiAp+6yMriFGUCJB4muGuzuvoeG8XM/L6a7JfA9RRKk/Vk5T7H2X/IlUTKabN4JuN0ZUa9Y7rn6vdkH8OlK5B9464g4+ME+0uF8hfCvDHOjiA+C3isFEgdjE51Oyim0r1kxqowymH9+8OLi6vJ4eDzaZ5qCHBCdakDd47LEjtLCxMU8QUI3jidIa2+ChL7hztRnL57IIMzc4XsYtByHuAlZGoJT6Q4mBKVLOAahHxZqEaMQBNQLuh+r7Wr3Ds+PqIOvRy92nn5T6WJAhz/uVD7EnWMvzCMcgQg2XHe7fizi0ETGoCLfwqKDBEhi5P/+H//4f/7nP3neiREx1NbWqWwbR4ahLwAJ5BU5tSpihtfVArcRoohCEoLnhRHHE0AAzZBs9w3qBCnsPCC1sCUKJFNA3w0GIUDAoitBdJJ1bW/r8u5WRcptiryB+oA7DMlEEOKeR62MAvr+EZbE8LjQpSUx+9wFe5BOXLUI1BuSJoZpLpKcb74i6i4/6aQXKY8P5opUpI9ct4S6JgDIc2vxsEWei8Z8iBw9P8SOM7vHyiVsmspMx998hWolPAgqvA3sSOkf7h/+Of05WTA7THK/AuSdcBPA5ckzQn/Ca9stkF2CzUOP+/b7FU7b7tO9Z1qKTXC2X53sXhT5AjkWrGKH+x6gI+UfFQmfU0RD8K3mHeB1scwyXIU0D0mJ1sXvCewsVtIwE4/DCtMl4Y6KUzkGseQ0FCmahvLdIcABWMxY1tYHiwOmwoZzq4z4DRAfIInRcr5YVXtSuNVepMl0VgI7HDLD7pac30LJ1zg4Kvr92U84gkI4Z4dx1sq9750pYRC8ssxI3IcqWFABOZ8jb/odIHwmTIAnb6VuqZdgGmewN4CrVHRcSmeQAkrnb0radER6EIV2UeUIxG6CAAT2j/HYG+UfkCP7/sfRtrBemYjCU+qGgiazKbPDRgdHax5RWSQB9Jcqp6S/8IALgpM5hUN2CSehN4dOpEKikAYrz4eScXbtE7MAvzXLuq3BUBXsxynp8kir1wJUOfKNkIEHQCsWfpYFHsaG9WCyzSdVGiFDRXpDmYMINlEI5+kdy+zwBTYiEUzg9vDP9GcrdM4jmoKzoPgQ5TeZi1HzqGRJzP3OryqlcNGqhejNGPbWh8rOnIcLLkcTfIgkG5YJdnGZgyTodc8OL7Yd9RYccAki2DGr3IhlhFc7AXNkSGoBX4l9T+inpSCkMuCyxGrzbGaRwfBXR4RS3Sy+2SF8sGw7fth2uw0SRWqqEcVuq4YfKtWAP8Stq7X2Qp/lWIGVp0kypN/5oNWXRtUI68ysAPEWeOIGylUDIiNqWmqTArhsoFwWHDEGaR/xHB7wvcKkCz3oklynRCRCPn4ODSe4ed1ysfo9QMc3Kv64bwVLDQbeLuGISJDI8iLk7/PxY3rL8gOIh1Del31NUwHvYGuBvIhlzYBEcRdncyR4obKH4hm/wNMCZf14qUhaBea8hslFcAO1LgI6ICyvzdsppeOwVXmuK8/iW/fYIQJvwcDBXCD1ob/ArlzjyUX6FzqikIwZZh4gBVEcJnNi8FDHBnNoZXE1q+4KNYtxkUQfXRC5QNCqjHK0FMFf4IGox/FtUhLiVSQwg9+aH1lF+FU25y08fHV2Ach/nQAzqfIl8pRa0frJO88IseekWvjTqcJjroznpDCCwR4MR173YAxLClvYG7JwMCItNdQax9Au0v2IdLUItcznCHZ0jrKlXS8QeTJ6bfuMr29TPhB+Oh3eVla6hckIxmEfyt/y6G6pGUCbn2xDbbNwF3BBj6XdVQUNP/x//9s//xcxpQSpg0PAdsLJrox9RzabFRRJPaz1f8I4E0MCuyC707YQI9P2Kyab0b2c7KeVzCzue5hxRA3vOUiVcZDV1UaiCxQsdpCclUFGOu2qOHYAgsjdqzzG+IrntPG6tXXa/6ak1bUata7blneTZ36JPEARz0EMiJj9IQ2OxvxqhU/ekOnMJxJf6y25ukPSqotCnwgR86J3LM4YSyK2FaeE+6SQbsyIOwVpymROPXrmkS6z7YDMZ7o2WbTScjWYIAyXQM+RI7OLJ4CIIFkplJe0ohBC9ZfYzfICiSMLco/v9GIW7GQgbQNdBiSO00i3TeDfL4HLYaUeDUv0e1RQ9fRREGHn+R1ZPINSWKKxdEK+sRIkw02aJj/H7hpfxqguQf7OfPaFhwpqik45jSwYYXRpPa1a76YfJH1TqI+1hlRQRCyQod0O1NWp+swLfGbd5LgbyyPNIdCzInbt5gzVWspw2HvjuzKuthSu0E7KNN3MchXzMqAZdglrMo71krFBO/AIajAeF0D47XHELdJ6bLjFSBTRS1GwcUsq2fzIhierxgXotvnmBIdbznaC8lWd+v7qVonyWWRrb9/QbeoIUIdVhwCXsh4jmtxhHYRCOzgHLiCFHQaMvVb2AxMlYFW82H+DrNBbbXlxNVDwFfi6knQ8ANHvizg0jUsa9yfvFdOEdkIDVDoSnVGhNAXLszqpY7s3A6LqqsxBTspQezNBlrJqXCD1pRJ3A9SiMdxVx1Qyca1vmtvSRgZWwyVaSEV1FUETFhAGjaUDPmuoS/oUacjfBjVJS+AK4SJH4Owit+9YdCyPGLiER5NOKsn8YlHkBSkggXNU9dZDkh7t3nC03rzk2Lxvy/sVjURZ6682RP/L9XcaZ/ecUgGqFio2Al48WQn0mWJtf0WjYU9KEi71skC7H+bQinKRVVQlerHRSG8Oi8jnCRAbFOwgqCI8n5ikHVwETCjypGHqSuehSNXOsqDZC75Ayfmi9F2Tl1RWYrrUdaU51oNYS7EjEunDxoCtkshWhYoUdKWoOkgkHTiJrHUhExyyWaImFybZaEzIp6GhMoHVKvPVW/N+/hWXmrkfYXvweLcQDatWeVWba/vts7jGWZ4y8ULIuA0MvypmmCy+kd5RT2Kc6tpIHaKD9HLXoBtSXm6G61X4U8IzpNr6CGhlmlbwLL+SMwLU1iJcjQeHvgreCyPAzoA4NlSt9kFYTPsvqG5pNaJsu/i3PZHYWBdjx/Br1DFAJ6zfBNAFH1GXsdGSKbL8GHfGyO4Y2E7aZcbp0EVciFbBLdBFtUBQ9FC73EPrT89b5HAUQC8X+Q06mZGPX2CJQHW1NYvtcFAiCS0XD7PrXNSDsil5BCitPXawj8vEsK/v2W0CiGqxEw9Sr0SbfshvMiGNdLZE+3ThLTNNoWwj+O3RnebCpDgzoGQWeBBV7wVSXZCNGDadchfmRx4ALqt79NCJTGXlrC7r5mXhsCxkvQgrYM9rwLVU9xjwZSsq4XukLuI20CWlqYpZazq+09Y7jUQZiR1EUx15w6y28ZJoykbUibwoWYhuXyOruySbM9SioppxVOyjSpAWQRjXl+EyXqQBmmTpK1kspX7d60smpkuUk4/rHvmcovSKL1G3wwc2kwYoP29n4sR3QUvVQrZFA2sc0vQQEM5cxgCLt5eE1S1ed2ixh6BB0R5vAFeItcI9IHaJMJX2FtQ2U18Wkk0I7DsoZ+Hte1Ul3f33yvHOmCwzxgaiffMgSxaoExArNBM41rX2jRGiTthEF3sdFAlaBvrc7uKu1u7izmn34q6cQav/cs1PsmrzZBIkgk5t9dAlJk6uq2ScIVkyDty5r7hvMc64eL5TA8U6EwuLZwYnR66rLSvImGe78OBguYLuyf1T/Vv2ZI1Zf7Dpx6yD42/wH8jf4Nx1gXZcDqzRTT5pKcOQjyumCBV/gAq01YrUlV4BGzHdg4RcMksNBGKHH1V5h/7k9Fvr5cp8GtMRSkPyUUMnNCBsWHVD9E/QrpduYyTNOXYFbVEwXKjDquMRfifuBW3Wg3o7qxwR6LDDBhzvXhGyQ2t+pF87afIhXjWA+wEVscCBH/eCoeLpB6Ph/2E5Jt8vmA9S9uP0otu9NgCqVgCr67eV1oWzlUXINUPwkZQ05KRR8cwAiaji9CAyaU3Agv11PsSySZFnxPRrykn8gjDCtOrqfocP9LZ6wONjWiRsXLaWQ1EpqzBJ+PPD+IseYTw7L0dV+w87ghyscAXRBkxkLzG4wUTCWBeGyq6YB6VZXeQyNHvFrgnDZApkEPkWK5Lf45agpcK80C7LWqsue32a5uMgpeEjT4UuoPJJAKzEIGcC7sHT2oAsurpOE011hOZ1xEuQnV2Nu0Q+dcXLGSDTaT5VZaBmVTsbaSS1a17gzZaATztoDqX9TGBYhqauowEhyKZprB1WrWeB9/muCffRg3lk5o1Q00YbVQx690D4bT0Jsihcu14b/a2axw9Z+6GsMMROtPl6VnMWrdeXqLu1LQ/zC6D87ZWitst/WLEbu7BYuZnNTq654i+AtSjvsBqvp32+HwyVZTPxA5SW9juDZbuycTBor0ia04a1vyviDMwidzZ3tcrtCobghumrPjHEqYwKVFygaGQc11lvF5UHebYjH5HVCEIyNbM8rhzgWnHXoG0+lPMNhfPy7/Mx7h70WFeWr0HXgCR02Rs6noW7YbcmjvCp6jZYBtpe7b7guCMYSqRimnuojq4FjlcCtaNlDqchG9q1ihexKMI2C1q1eyjHQ3Do+EE1Gop6sMBEh+8FKHGQBPMlnMVpPA7QcKrfux4TrBk+0FJynSEVR4uDiqtFxRNoFlzHEqS1j33Zdj0xDhq+GO2MV6unRjV+cZU7xkr3i0/a59zXA/drDudA5FUfSWAw6Sc5Pe5Ol6j+3kX/JfpvR1c2jugEEo4snzywQ3bipTcYbRD77Igct/JU4oV9ev7d6OXx2bGvdaUtClQEQaTayCZ+l2wZp2hBG8Cei3reTVBk8nwXoy9+zyuTeQy4OV8MvHGeRtv+Ki9sUZKbz+iV8t//l3f4sqpdt0JTLTrvZgasAjreh7OrwSb8TJIs5HjCVs0yYP8kmS5lQwm68kxhczaCSO1ikJgGiDqbqt3UeAOkwZRMyjG7zsdVAx0vBGtyiGKJQ2VcHsBLdocmFDsWE5AzQ/UArgF1Zb/zC3Z14M+vld/TQdW/UDQ6nV39aOz/7W+dh2HihGiYXYJJas4SJGoY+8DzEcMBwTHEvT/LVTl4uvfsq91wRlFX2x3yMnLPJlEc1VzvcE4bU4Az5MMssHKJp8Pn+ei7cRqIIur5MvwQA7myMgWr4VUc/oCuP/D3Ml7k9IPL0k/0uKMfGH5uHl7oiCvqggkSEwvNhKLXyn4dn4+er4vP0fjK6+JxQIPa3hBuR+P30ThsWGdRbMNQRTwvw1qEZkWcQwgULdAGQarydym8CJS6rzB+p3gnXQEpBx4299QxRaRKmozd4q/Jas+iaJGgEpl8bY1pNEJOA903idyOdwF7jricBgj7Og7ata8tEbTWk1u0RWOjN9IAkXFcCY6nGZdaUkAEsrHHKJN6R2cOS+BJ2GQ0Rrc+FImTdneqKmCbQYIYWAlLdkDEFD5nOwynz+oO1yOIjdMHnBTLeUbWRLZysAMWBQz5pydnJyPvqU/7dxKQHZmV1sQxwKalJcuBpQNS5CGeDuShnCXZ4I9vkIa8HbxBcRn+aLx8q2su4PO0iGG8UlteALcFVKhSe7dZ++We1JrtDSTLiec1tvHwz2vvY3W9qYMJQAE3V98ZjlMKN61zLCA6+T1fR6dw7SKu1y/i6RLjLatB8+T6ohzgBsjHZV42gPBLw5C3dqXjdgUGHywaYORtAw6iTJQvkTGllpQFhAjMG6MCSb++v0tfuF2axQEFmz9gytMu7+zEQxILTJRUdqFjTQf8SoOOAIevjtvkPYDe50m7u4h24tG2u4rzDulMakEnBqT2SbRzyEHDsvxNvy6LIXrgOp5NuZMpG3vIOi4T6c+6uGRix7M/CVJFUjkSQLE71XcfBgZ9d7juBuS4qM3sQYa1sQVwrQXOGCUI6zPGyQFdmxsrw1uQvtR4CxmPcS15CFPIXBlFrlXC2jysT+41d272mdINNUOfe9ibTwRh8McII0yNW7wOjGNBiYQiI7ZQWNnRD33vhzgmd5Q5O9qB+KVXhBQZxXIhTaMfUMkeG6HxMaRwN/ZkwW4HBcvZFX6SHJJCV6tilRhw7PP5iv189+7dB2x45xkOH54aC/3icu0VnhQbWtpJsRKHabroK/nBQS+Zl6HujJdJGjWcsAXkSp7BLgJHLQbe1RUWvhJrjZE0sTWQIzH1V2VE9ak7+2ntqZvfbmjq5re17TC6F+lBYAR04ljUiedDdZ0vw81sxKZK/tjgfGG0wps3gUEr9wBrY9QBGr23iUV0AzeBasJo5d+hguHbHwtH04g2WPob7eQwxSYfgpc3+CDjU0KKVPY2YW8vm8JM0IOojMmPZqywrvXVpzZ8wZl2RGkJUkkrKljjUorvcThs7NIN8mCifA7ypQRUSjYZclS2fDVVlWK12qICcD21sDS/NhHxjpBtJHiWgclXDT0iB0+ML3OlK8AELOIQs68Q4Hz8HjGwXpmyKJ2B9F/cbbdsWNRbr7tjVZ5taMsCpM+TZ6jf6HJkvGzyIplSfgME2veMyY7zPbkoyUYsUhLoDkhCBmmOGnGVd5z9yuSrGidTtgZyVC2cLtxIG9Pw42jt6b0pNyWWACgKiW53tcMZ0MouJvf+1ZWEUEe+nfQo4iAE68vU9wx8FYdF1Z3M6IrlE+5ujlx+Odz7+pse/Hn27VfIUL4cfv10Dw4UTHNFHESm65A+m1RK0C4PwWlzsRzXnGgPPHgHHAO7wKKxqFrZ684pDF6YFtk4d9ZHGaAG6XTbaQQe3RaGvNXEKxc+Ar6Vs3m9HTorYc27L4dPcIiXwwPXc64Pi4ijVThWS94MOKXVV+GyoLiG7/985vSJzUHtxzhFrtXclF9jfp54kSP2arWvjX1qQddTsvOuibBkHN4MyhKozUpRBFLMUvcBlamz2l4RnyipImtPFrnisHGi3uJYSMqUYKzqjTUk+sumNG9saBoRrVA/jssbTCXFTSuebQc8C2paFqVF8b2//lX9nS8WM6csjoTVE/fLz24OOE6MVReibf+CRG/Z+uToGRTLFrALjVkRtqieR0sL1jgWPRRHSFVNKdXgy6Ok1NPh2qi9KbxWtQiLF5wvyKSpEZsUhu86eR8NR6DTMrAdXOPGz8mihxJTDxM06PQPNneXu9zkOUznGjI8zWwQphZmNiE1IH/QUHF1bysQDSsF54NOZEeF2Jjt+tARwH9/csHttnAhZ0cjKrjm6ohPx6ZOSwHXID/DMMg4mAc2aoFOJ+I+wt0lowcbL6wLomoEaEkKExiOhGdUfFCaU3J4sfZ0hIuNTUW4wPRgSmLnVpANg092qbHbVNMzgLAUR/Lc44y/ApSph2TgRxwobm+bkYnZQZ6PXp2ftlGNrUkjzLrYLGUL4xMG8/jkySVzC5FJd4vNQbntwZMnW0+QW8KhXl352uti29shgccN2tEzQucyjJArcl5Iqgp9wXoXJtHGblBMlyb3AYl5wn0hHOwYwhSJE31PTW9guWu9OTSlPLYDdjBVcN5h1wCSjTXVhxcdthLy15ej0QX6P1w7ylNTf5tbBKG41qCZM84jiOAEiIKvekIELHBCC9htJQqTMgQ2IBFQDuKhORWzEimQUlgFydrIgqMNWDC9/OZL+Cd9DOfRfX2k8bCNeEeM6uzDikk2gmUmcRS21wxVkM5dwYMoSkT/ZoyygRL+EF32i/RO5/Haubw4BHk6gVVmeJRej6AJS44QOXWiZOQT4QQzXlegmHR8dvh0GMrwcdhhuQIyf9RbjZPqoNNWmmDuOgciKbplPqnXJ9kkdwd/SG8pi6DknfQUqYuWBTtkJZjSRbLBO4B/wb0CaCWeQNgTBtXp6dSE8PVp/8v+l52/bWv/CmfR7D4QCuDDgvuSJMDqKaM+VRSMXFGTFEDNmnXabHIKrUeeMTnRZgg0gcpydq9sUtQZKftbMh9BBT6kEIlQgYDOK3mB+lRYfooi0+iELqYlpSDT7UFlyYpsI4d0zA8nOK41VclRY8UPA9A6FjYAYmbl2M3JVCwzT5cHdpQdKCnCZyYcTEjeWKzGJEkVaiSRcJCXbponn2H6YnzuvssX+FeSrkqL77b7TtJzOk/mqPoZx/VQMNrZCkgzmZelv240kWbl4CTredM8SF1nFIS13cCwLPocxQz7mG4GxRjWI3XR4tz6KHU0lzVZz5ugrQtxohil0CmSXTgNUyWpMXU+G+3+2aXklwGe58pR2FR6h5K39OJB+cLOwqMEjCwiV6S1lw3dETe1bAjr0ctGfpCPXDYs+9suW6V3smz47hHLpmfhUct2efDjy+O1DRecv2tTHDdDY62FtWC8MvH7boIvx7t2Qa4aKHHoXEai+Wg4SAzX9ttTG/PbU7NmqPZoZqTeUl+8wenH9PUZmIFMi7owPT124xTHZsNCEDLUKHULty8ebE7WcWTxKYKpi+xeQOkPJRkkTeR2kx8UfIV6FDyjeR/XTbg9UoanVurhIePyYokTPko5KCjerRUOp2KSO250Zi8AK0puh8dNasGjuYkF4jFlKk/1hO/KZDdjYxkuNnATudDdeSHdxE1ekOO9ySTZQD9M/7YuAmbzjUm0kofO0RhTj4IoIq7HJKwu5sAGRugc+bS/13/W/2r36TdPfc8CwWEvgZu9qyMzwbNf604tPmuojVsLwrIedRWbpX6NjFCQlOJZWsSYWdtWZhOWaicX+iOMK0QPQfQPAcGaEvobCDb7uIHwZ3klwiuNScuAZFja63nP3CGAKKZv72nzym1GCdC1AjhBQQkVxhQ4aG2BbbtZti5H/AM6eN4TNDIkFAHkOvpiQMHJEX0/0wFkzIPS4WRThTkFGznwTbwAlNDRTe0ljRLBlhQfIR6Cb2bIx1h6sxB62p/RsRRTWga2v7RqLEw7tBOOOYpyxQ5oYr6e7leoktBuabICpKYQPCUb6bKcmdyqkpRxMvBenh0cPt37dvjyYG/vqx4//v0ePpI1Bx7hBzw++1a+wg94/Pop4MrZ0dc9OHoOJGYmuW6B/+zoeNjzDo6H0Aj//fs9+kvw4aPtHIjTi1lBLq80FHyZF1qqXNjPzqio1XpFfImhDu1V4mwKaHOi8y/zI0gTuJ3cg9hGEtSIHXqMrknrPqabIXUAiA/TVmOHCHVMIUyCJOwv5tis7dbEWF4pwSZbtypxMF0Tn/Mx1SN7KPOLLkfKF+msDgHdojvf6uZ/OqkTELMy1EQQcYhMJ3X6BtcS0vE80lABgxgXuO0G5pClAZn3ZkSV6kZ9QsTjObMgf1rSGT9wcioxbdEsykcuUAX1FMHMg9tDMpKcAibBPsYXyXw516aTlF5XK35N6iv1IVn8OANZmIxNr+KgeI45ki8CNA5iIlBVG9mNKexhIhhOqYzclS6/aqSSeWlgZsTkYopA9CfHgqMxbmjkRY7G8ONu+DHtea+e4v8XKT1ciJ8v/r6MIzVLJiDyDqH5D/RuWCKnVUTwQEzeCH5s1zfPT+ubaW43ZadJMgClZnHcYFW1Spc/ls41LF3HbGW9T6mcPigBKCYiQHKxTIOGGpxlFIoJ0uyfKWoykkomA9RGuyvoeqBCM6u8KXq1rY8uJJITM+jpZDrWvYJHyd7i2g9Aixk9OLZ67CJqJsu5ZKiHaiVSLrK3zhVqCZExppQ/YazT7Piw4OxxjnGD7td6g3Z0fG7UvIlpC4nZnHsDeIEJL6Ub9lC110QQK9M56DgzV+Q3dT4MX30O4KfExViEPlv7NLidb+g0AEB8Y1M17R4z7ztNJ2LsaxlMqUdy1ZOAafrRHGixQ4yA+AdWzoCQivoKunZdJec2ZHkfJ9qpXYtL0zk7KDptV3IbrAhSe855BmjWd/Na0YcmnzMT8JWgNmnBZtbDZj1Y6ZJjbkpj0/wM8A7L706XGVaDAxpvDGVTLXmyRcuQa5LXjHDukmnB5BuvR/HpcIlHz45JfELVQnV9VUFqr0vzZ69pwZv7tvXFDFSR6lSak0r8jir0YVL4msBYfHkpYbBa5OIiZHtVuwhN5AHSm0KLxHNI0s6qp6FoZTcTt9XUa2h/5HCWY4JVzR0cHb84eH066nnHPx0en/a8k1cvzi/PTn66ev3q9PzgqPHiClYIDtm/wFEMZ+mLw6+efvtlzzu/PDg8PYaD9nw4+u7yGD5yOed5dPzTiA/Xoxe0BXHlXq4OoDA3bmDMEVpu+HoTHUth7bE6CKMCksJxJXXigaEiM/lWx53aWpobBCly5IwvhXhoIg9OYULgv6tX56+uXr3Gp7OTVydnB/CDX50dX54c0hTA83Hf9PhI8h7UsvMK4thwFEp0H4KcmM9NroTCM2COG/ExD8DQATVS/9UyTa1bWe2Hjd5oia9f2iCUx0eWwq7a7/xiV2xA3EavMiED/5O/TlwpE4D9TtcBy1xMz67lAFcJY0n/4FywpHh8QESIGgR8GUFelKF4UljyUsuGPzLZz1YFJp+djpwcaY8j95j0nO/42gg5QHCUo6lVBKplbVozYrnC9nHSNn0VMbzknJsV9sUKKo4yxaaHw0zylnl9+uWXQHyBKOw8NXIfldApBpA5pJDGRuQdJhNfd94xc/N+v9/fzLQjNAquf1D4bEbNJxO5cnTfM3mIEF6mc96sSojPNk9r9pCYF/ptp54lAk7UQWkA5Z1OXR06EqSbTrFLiipyoqAry4DT4uI2L/wN2UVWZZO2zjCJ5PPgywj5shVxwtc98QI+bClugFNwk1hvmsHBsVi5+fb+TrtSSsPS7ur5/+zmGGS1GSSxvAwbbUqDrbWGJ8SGW6LrYXQrQGx9sxt8zesMvE6X3LgHHnAJx696OqvrwLs8Bt4DpQH0s3hxcPIKWBScJsDggXcyOjg9OeQXUXJdKQCyw8DzBz5T+BPZRfuS7V3ngZFcQtRfLdfpu3utQOomlHI2hgAeI0coNzrSTQZsSqSZEe9caJiF6K5Y+vSpta1NFSDoJGg2MqHhPJsIgqeRoUpmY0ES7MDIZckx/7YzveZMc3lR9wpCTudxdPLiheTzaDPrNNxUT8+EN1+XqqLHajpnycUk2t4cmeU02nUJvDWPCc5YkJjExsyI55NK9u3qiUYzdXZ+dHxq7khbqVE0DL57aJrM0Th/cm8wy+MUmZjs5mkazIPtnudTenG+5xyvGGZzC93eOteZfdQNMLA97RIkIgxD4OMZs1t8iO987YQGLeirILd0XnXU9MtFDY7rr2SHptMVoWJ/q4UZQILp572q07DeWHI5XC3OExPci+RLdSVHEf0m3x3tRwxvMX6Lt560g8iuE2/pvBy43UwaPJwfuyr22kxMG4yXC8nJ1m65pOUdHh9eHl+c98zT89eHPxyP7PPFwXBon86A2uAb2uX65YuT02OQg6jVpkyBB72wOI/NHcLmZk4fQhGlowQo22uRf3y5QWLftxdQO9no62KTaDK0IGSvUIRFVjqhPfcXlbEJKuOA1ARZ2Wdi987ug3d1P3nMOoHf7Wf2W0s5kXklFUulW33L2g/cFnR2GMbrHiHVgH4/2+7wrW2WjHTwzrAcj+w06jSCZi9tEvJ1qZbNX745UmVhEsrXSJbOVW3pPvTNZNDWu93Jq241txXYFXWYCb5uhX77GOhV00GPjK09DyZlu9bwA7aWqmTBVpfC1VVTA3tGPQOSZpgo1zGTbRD1ZtF5JwxUee+w3cO8MuqbWSJhdBpMc/9WcyYNMFib0BA30n7nzdPeXu/Z247TpX3OeN/Syf0OXTKNnkBd/81Xn75+6yNSo6KJ9Lctkd/DuFwbe4EObQ5tAVg1fPbA+ESJGqR66Y2Tr91KEcsMHv0ecyqB/jE2b1C6UHdzdqd0+K1tKSBCiMn+7ld9baqrg8MH5nIAZ2PU88YDNGjDNNNA9qldnGfgSu0bCS1ElbGO29dqfP8JXfiSoHkPDY4YRsISFd9oc0uXpSYsKtF12CgsrczrvDqF2Vo6YcKZDTlDbTiFGd0/0KLab7n3yaTQt35+HC5prrjjbcu8GgNfZnxXwH1ZjOwdNGMbdskVOeIlc0N5tfLD4f6picfrszaeJ+3zs6T968tmdkhZMD8Du6HaphAce+CiJPfJuXXXcbYC4avzD+gw9QfvH/AAGZAwN0jjaZxFfwC66r3ZmSfZ4Mu3+CO4HTz98su3HXbNRkIHxIlQztrixJaJ5IsoXhw+lb978veZ/MU/eD+d0vcg+AIXO0K5G6opYEv9DXjB93O0smO6fGb4upL8yPbDJMRD8yZw5UBqBbANAFW/Z1L7R7+tohsHTJtSXNJ9miO3Sp65dh9oiTVaZL+VOjyjbiXmHMewF6WqvuIs82QwZOMlH0N9jYYoHHFBwxV3q5DyIyzwJNJEIYQiOtyG7w/iZCycQcScJlv6sA/n0X4nRBK3ozzZhMEioVScO8AQYzR4Xkx3E6Xg8aaP/qByshOq6f6xMoKe9ju48LCm6koHqvbx5ogSBjcYw7gHeBFd9XOeTfk79H2ATx26x3L/6xbO4XAefcaum28oIRAAq12sNmp1XLX3KaA/gLdIFjEerj03Vz97osFXzm1soK/0KZer6+wNUpr/Y2GQQiZ1JKRcfGLODnsnADei4o/rt2HSzzdy6aFZkK6TlsuGuZGsPRCc5kDPmcXQegoKUvuaPH0mT7oDn2L7Hjg0dXod7elAoSyRJfcEyBeHA/+XvxE5muTLLHIaKmHfrNWO9lkxXvmc/fd5UAANtGmBRe/WmO2eMQZYlNhn7xLe1rYxUXbT3T/NOUXcE5/9lnSca++jaLwhM8KvTMG5RgLONdNvrp18c8OpN6F9yUJdaT0aiysMLChLd/Z4AQaPLxLtSt519wIpNi6MsxwOCs5E3UTiLBfgVCahTAOMSI193pXMvKVk08D0ZUZaYEVRUvOWisZJGKzwkhQwGB5PsYCOH5RkShDXmNCu/Bhv3Kpb4ZwbnLRNje/qZlW/tr1RXZ0EHmfNxORiGEeVySSER2dRnbAN8wbF10l8Y3y2q4nKyfUCU9bqCADcG+wFqeL/f3J7rk8UNpXd89fk9txEUs5NJ4GspIAk8Hh3JifEvzcLk86hL5mm7HkrkpRJ/+6Mgh1MdUOPzOPpXHOwRipPgJ9V3ANfxTdU0bAAbbP818xvSUhxjOE9a6McBvdsBucQUo2jq98G50ySEWkdjg+pnhzKLeaZat4YEEhge4oUFLMh5Mcki/IbYfixO03/P6dH5vbVWuSQw/K3dYPut6905YqU075tNstNwxZp9C+doh36ShdOcmZJODicDtcEqxb3Z0cIIYsIOnvi3c58ERknm/D1+vogp6CgsvXFv9kdJ9mumm2hZ673uy6JLjvXni65vRWHsxwOXqwvhhUT0f47HijnzLzSESE7tfdRojhGBj4ytHNRprDWD5Ns3Smg9BagusLTinqIIZJyQ5Odj/1OR3yKHRYtE+9cncYMmQV38vfd4IGGzgHzXa29VzaW5ey3yUGm4MTXyb0k0+FNiUEkzURkElnSyERGIO7PQ1bJQrZ+DjK3k5vOQEYRTRwGzxooagy5Errgg/U5Oo+K3NYqsVrBMkqI/nZ5JNv0NkyDBFrrngULfgFUKNHXoRzBDPJb7Upoy4GQvsRDwAWG94JVnrFMdFAypAL+8fssL59zCLLThFpK5kQNoYbf3+G1Y2sjOF5WthkMp2vPHqk0duJbaxmIAhg7OfnTK4wbJric7PjYeDHxZyGatLS84lhBlPQ1f1pcekqIwDNBzP3Wsb6C2pLfStcENfwyKdPYl99ssseLX3pYt8eqmx7wx5jiCvG0hxbpoMe+MXjSzSNew0Ar1+joS7XzMCFsV7RwopqkzPfbHlfLgLKP3/tcxn539ZOJc684ZtIndRLZ5AgEHpokq3fRGXvpCrx0GulbzUidQmp9DLltIBldVbc2kuGdM5tBMoREbjJhec9RS4mbWx2WWHohd05zAZXNcQXFrKBJ9/PZu4EcZyFu/iaJULFqr+/BCgu+/uk36hj680CDieTqQCO89iNK41KcKXTX8LBu7ZruFbsmVTI0Uh3XLKI9jEhjMi6A9aqba4nT7XsjxgGoh53YJxeQLI4N06d7xA5RfdNLiiR2ZNODeb7M2DCkeoxSpirrdNzwY9oBxGqYzulRBJijIdNJxmkOddSBJ21HMN91rt+6ldB61WQNuxRLNnoiBPK6astamU/X3zppPt2QV1Q+pcsP6zcJubdCm+S0SIaYKOlgep0kp+Ib15Ihb/0Rbkq7PI+cG8NWe8Omd5JQiEoatal4D2d37n5sjO/ip88hf4uNxUkt7o9wMjlwawFOzUQiB8PDk5P1R6Lv4NuQ94tAUyVTTbPph/gCx+Z6KWPW00TBsrEmXldG72an6jHzx4+oCCe1W3EEj8RLS6JER+3BhL+eFIym59vtCswsX53Ctbfi5kETOmnU62MyGCTFijsMqw2KF2x7i840OA3JbFDIYg01nBsy18cO5xrODW1pBtbEeI6N11F9+oIXzljJ/iJVeV0AicAuTybZl4sGlIv4mHIRk1nGZvBzoTdD6T/Toq02aNJWv41NG2aIuM1fZ8GmFGqrbdf+r7RYk3DaYrMVB60ujKZ3N9jb/gS/9uDX1/QL333V8u7r7bcdD7MfpGxpk2WTGd/vYG5I/83b/q1PRthberjz2WJ75/E0ejvXSF8G33baXLr+9BnnyiaTMbQaTvilm39DM1k17X11t5EhRW4w/ZjeY8CYNYwYSlsxDK1yLm2KNMh7DTLEAfNnCYA2d58ZqJaZbuzkUY01ePxyaKZiM2viMDPOQW/do/GUbzeMUhoecxGqvHRpwMTeJYq2WV8rqD233bpD6snEvYG0dmMImnccd9K2VruwN3eM1yKdZNV7XpnjatXbVmG6jlz6gozIXlVRzUbttiCGaLcFgzHO2Ky9oJIdbtXA3AARZ30aiIUJI9bGqY2ljABA9bSYIyvH4Cfa5jqFsJ55N/cm1a970JFsZWLWzoe+8o5vwzjdcS7Vdq5J4iV/dPaKih2kRlpwRI2wZrkqGbYABWBUnBPNuVc6ucudCRA3uC7eNzsQBmpMDsnP87LM5wOvM4+jZDnvVN8f0rmJWYmjzrbpW0PcNhk06tcgGCH7wdlblVNDmqvLzjilgZGfkd3pcfBIrxLYUVthTeIf7g2K8sS6s8unjEKUDnXs/xzBaWNyEwD7dZITDOI//0fjOrm1JeoNE8xjnCpL59Ie1tCryh0tVQ30wmTTRpRtDUmh8Zus2yThXFvT1MqpaExDe9ItdPQEIG7YCzzKaFDwQDezXJyfjD+oU4NDY5w6Y/5DW1kqXbAPiRs906xQcSRx414+NQNtWntYB/CCyV0lIMeJA9AQCF1VvixC099jih7LdgOv7aLlpVoVPBQ4eCCgfiC7C4MakQba5HCsDh/tMCa119YfgHPHK77naAPAbF73XPUNEkoYL0q1a7HTIu1/+q/auViRPRD45dnWFxKXrf032K7D4YDobLcF+9tDd+LvVZ5RvBE72VGozdaD1eXa+LAANIdv90IzSTXkQvUU3Ul2CmqkfvmNcQshcuPeqk5NkEu1pPgV4FRH8g3b9B3AxLPh0c6H0bQ6N1vwYb5FgEiHiWOg/s/lasX9eYQdddIISZgTiJXocaeYKfkuz6cASHuK7pya5Ryn+bQ/pc+7BaE6tUbOux140Rc39H4CNLqDnO9+R5sN0CLRw/uE0ELUsfPIoZpfADJ+QJ0nRu1RwBRR6+V8jgGmMa0HmSfYXts5QJvvnYfuo2y14MwlUuFnONl055xIK/OVHJc6TtSd8VSXkC3uwsDTQVv1+dd+IWJ/Jp8vpTXkSVGLvhE79JY2ZfN8ybagLNmqZUJqbQjyUnQGWj56NqRGORim6q3AU59rdKoo1VLOQny4rDRXK6j7LRHSkqb8w3IMOJAyGP1pX7+tVyGbESDJFMmOz9PEwTNqOd5xvgA0vwaTSw8GtPPvB4zNF1mMYn5Lx/jLYIAj4gS5cujLvs5RV/WB2CGuK06YihHU38LtPdjy0HiAf3a8p/T/Hv3/bIs0GB7sjhJE71+824H3R+9vnS1U6g7IqLWFpAlrJrCN4N2nLcwHOjw+PT4cIZ53O7edba8zfH3WgS+5wHvz5ds+vfI1/u9MvJ0tV4JHKvvP3jkpQ2ZxugCRNeR7MfDkfolvVpzSLeYtKL2v007Dsc+ijOJzIIrLIEkx5co0KCJNF5ecrbPDqNTzKJxsv0Pr2yGK4RnIjky5DmjBSF3bAWhQwQCE9YHtGaQWS0xhdHek3BeXcvcMuTvq2eJjz/UhfEM79QS4q8NZUH53MfLygxcLD3687WoKCgSynC7KPnCou9Pd6c7y+evgIl2e3uzgYm2z9yHL+sYBe+v/AXXB8J2twAAA"))), -'docs/FILTERS.md':af.fromBytes2String(io.gunzip(af.fromBase64("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")))}; +'docs/FILTERS.md':af.fromBytes2String(io.gunzip(af.fromBase64("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")))}; var _params=processExpr(" "); const oafp=a=>{if(!isUnDef(a)&&!isDef(a.____ojob)){isDef($sec().procMap)&&(a=$sec().procMap(a));var T=b=>{for(var c=Object.keys(H),d=0;d{a.__origr=b;c.__ifrom&&(b=$from(isArray(b)?b:[b]).query(af.fromNLinq(c.__ifrom.trim())),delete c.__ifrom);if(c.__isql){var d=__;if(isString(a.sqlfilter))switch(a.sqlfilter.toLowerCase()){case "simple":d="nlinq";break;case "advanced":d="h2";break;default:d=__}if(isArray(b)&&b.length>0)if(isString(a.isqlfiltertables)){var f= $sql();d=y(a.isqlfiltertables);isArray(d)&&(d.forEach(g=>{isUnDef(g.table)&&l(-1,"One 'table' not defined in isqlfiltertables");g.path=_$(g.path,"isqlfiltertables table "+g.table+" path").isString().default("@");var e=$path(b,g.path);isArray(e)&&(f=f.table(g.table,e))}),b=f.closeQuery(c.__isql.trim()))}else b=$sql(b,c.__isql.trim(),d);delete c.__isql}c.__path&&(b=$path(b,c.__path.trim()),delete c.__path);if(isString(b))return T(b);b=T(b);c.__from&&(b=$from(isArray(b)?b:[b]).query(af.fromNLinq(c.__from.trim())), @@ -149,9 +149,9 @@ y(a.llmoptions):$sec("system","envs").get(a.llmenv)));isUnDef(b.getModels)&&l(-1 x();plugin("JMX");ow.loadJava();let d;isUnDef(a.jmxurl)?(ow.loadServer(),d=new ow.java.JMX((new JMX).attach2Local(a.jmxpid).URL)):d=new ow.java.JMX(a.jmxurl,a.jmxuser,a.jmxpass,a.jmxprovider);let f;switch(a.jmxop){case "domains":f=d.getDomains();break;case "query":isString(b)?f=d.queryNames(b):l(-1,"Input needs to be a JMX query string (e.g. java.lang:*)");break;case "get":isString(b)?f=d.getObject(b):l(-1,"Input needs to be a JMX object name (e.g. java.lang:type=Memory)");break;default:case "all":f= d.getAll()}n(f,c)}],["snmp",(b,c)=>{_$(a.insnmp,"insnmp").isString().$_();a.insnmpcommunity=_$(a.insnmpcommunity,"insnmpcommunity").isString().default("public");a.insnmptimeout=_$(a.insnmptimeout,"insnmptimeout").isNumber().default(__);a.insnmpretries=_$(a.insnmpretries,"insnmpretries").isNumber().default(__);a.insnmpversion=_$(a.insnmpversion,"insnmpversion").isString().default(__);a.insnmpsec=y(_$(a.insnmpsec,"insnmpsec").or().isString().isMap().default(__));x();plugin("SNMP");var d=new SNMP(a.insnmp, a.insnmpcommunity,a.insnmptimeout,a.insnmpversion,a.insnmpsec),f={};f=y(b,!0);if(isString(f))f=f.split("\n").map(e=>e.trim()).filter(e=>e.length>0),f.length==1?(f=d.get(b),isMap(f)&&(f=f[b])):f=pForEach(f,e=>{var h=d.get(e);isMap(h)&&(h=h[e]);return h});else{b=isMap(f);ow.loadObj();var g=e=>d.get(e)[e];if(b){let e=[];traverse(f,(h,k,q,t)=>{isString(k)&&e.push({o:t,k:h,v:k})});pForEach(e,h=>h.o[h.k]=g(h.v))}else f=pForEach(f,e=>g(e))}n(f,c)}],["ls",(b,c)=>{x();if(isString(b)){var d=toBoolean(a.lsposix); -isDef(a.file)&&(b=a.file);var f=io.fileExists(b),g;f&&(g=io.fileInfo(b));if(f&&g.isFile)switch(isDef(a.lsext)?a.lsext:g.filename.replace(/^.*\./,"").toLowerCase()){case "tgz":case "gz":b=io.listFilesTAR(b,!0);break;case "tar":b=io.listFilesTAR(b);break;default:plugin("ZIP"),b=$m4a((new ZIP).list(b))}else b=toBoolean(a.lsrecursive)?listFilesRecursive(b,d):io.listFiles(b,d).files;n(b,c)}else l(-1,"ls is only supported with a string.")}],["mcp",(b,c)=>{x();isUnDef($mcp)&&l(-1,"mcp support not found."); -var d=y(b,!0);b=$mcp(d);b=b.initialize();toBoolean(a.inmcptoolslist)?(d=b.listTools(),isMap(d)&&isDef(d.tools)&&(d=d.tools)):toBoolean(a.inmcplistprompts)?(d=b.listPrompts(),isMap(d)&&isDef(d.prompts)&&(d=d.prompts)):(isUnDef(d.tool)&&l(-1,"For in=mcp a tool needs to be defined."),isUnDef(d.params)&&(d.params={}),d=b.callTool(d.tool,d.params));b.destroy();n(d,c)}],["toml",(b,c)=>{x();isUnDef(af.fromTOML)&&l(-1,"TOML support not found.");n(af.fromTOML(b),c)}],["toon",(b,c)=>{x();isUnDef(af.fromTOON)&& -l(-1,"TOON support not found.");n(af.fromTOON(b),c)}],["slon",(b,c)=>{x();n(af.fromSLON(b),c)}],["json",(b,c)=>{x();n(jsonParse(b,__,__,isString(b)),c)}]]),G={};isString(a.libs)&&(a.libs=a.libs.split(",").map(b=>b.trim()).filter(b=>b.length>0));isDef(__flags.OAFP)&&isArray(__flags.OAFP.libs)&&isArray(a.libs)?a.libs=__flags.OAFP.libs.concat(a.libs):a.libs=isDef(__flags.OAFP)?__flags.OAFP.libs:[];isArray(a.libs)&&a.libs.forEach(b=>{try{if(b.startsWith("@"))if(/^@([^\/]+)\/(.+)\.js$/.test(b)){var c= +isDef(a.file)&&(b=a.file);var f=io.fileExists(b),g;f&&(g=io.fileInfo(b));if(f&&g.isFile)switch(isDef(a.lsext)?a.lsext:g.filename.replace(/^.*\./,"").toLowerCase()){case "tgz":case "gz":b=io.listFilesTAR(b,!0);break;case "tar":b=io.listFilesTAR(b);break;default:plugin("ZIP"),b=$m4a((new ZIP).list(b))}else b=toBoolean(a.lsrecursive)?af.fromJavaArray(listFilesRecursive(b,d)):af.fromJavaMap(io.listFiles(b,d)).files;n(b,c)}else l(-1,"ls is only supported with a string.")}],["mcp",(b,c)=>{x();isUnDef($mcp)&& +l(-1,"mcp support not found.");var d=y(b,!0);b=$mcp(d);b=b.initialize();toBoolean(a.inmcptoolslist)?(d=b.listTools(),isMap(d)&&isDef(d.tools)&&(d=d.tools)):toBoolean(a.inmcplistprompts)?(d=b.listPrompts(),isMap(d)&&isDef(d.prompts)&&(d=d.prompts)):(isUnDef(d.tool)&&l(-1,"For in=mcp a tool needs to be defined."),isUnDef(d.params)&&(d.params={}),d=b.callTool(d.tool,d.params));b.destroy();n(d,c)}],["toml",(b,c)=>{x();isUnDef(af.fromTOML)&&l(-1,"TOML support not found.");n(af.fromTOML(b),c)}],["toon", +(b,c)=>{x();isUnDef(af.fromTOON)&&l(-1,"TOON support not found.");n(af.fromTOON(b),c)}],["slon",(b,c)=>{x();n(af.fromSLON(b),c)}],["json",(b,c)=>{x();n(jsonParse(b,__,__,isString(b)),c)}]]),G={};isString(a.libs)&&(a.libs=a.libs.split(",").map(b=>b.trim()).filter(b=>b.length>0));isDef(__flags.OAFP)&&isArray(__flags.OAFP.libs)&&isArray(a.libs)?a.libs=__flags.OAFP.libs.concat(a.libs):a.libs=isDef(__flags.OAFP)?__flags.OAFP.libs:[];isArray(a.libs)&&a.libs.forEach(b=>{try{if(b.startsWith("@"))if(/^@([^\/]+)\/(.+)\.js$/.test(b)){var c= b.match(/^@([^\/]+)\/(.+)\.js$/),d=getOPackPath(c[1])+"/"+c[2]+".js";io.fileExists(d)?loadLib(d):l(-1,"ERROR: Library '"+b+"' not found.")}else l(-1,"ERROR: Library '"+b+"' does not have the correct format (@oPack/library.js).");else{var f=require("oafp_"+b+".js");if(isDef(f.oafplib)){var g=f.oafplib(clone(a),n,z,{_runCmd2Bytes:C,_fromJSSLON:y,_msg:"(processing data...)",_showTmpMsg:x,_clearTmpMsg:L,_chartPathParse:V,_exit:l,_print:w,_o$o:z});isMap(g)&&(isArray(g.fileExtensions)&&g.fileExtensions.forEach(e=> {var h=e.ext;e=e.type;W.has(h)?a.debug&&printErr("WARN: Extension '"+h+"' already exists."):W.set(h,e)}),isArray(g.fileExtensionsNoMem)&&g.fileExtensionsNoMem.forEach(e=>{e=e.ext;Z.has(e)?a.debug&&printErr("WARN: Extension '"+e+"' already exists."):Z.add(e)}),isArray(g.input)&&g.input.forEach(e=>{var h=e.type;e=e.fn;K.has(h)?a.debug&&printErr("WARN: Input type '"+h+"' already exists."):K.set(h,e)}),isArray(g.inputLine)&&g.inputLine.forEach(e=>{var h=e.type;e=e.fn;isUnDef(_inputLinesFns[h])?aa[h]= e:a.debug&&printErr("WARN: Input type '"+h+"' already exists.")}),isArray(g.transform)&&g.transform.forEach(e=>{var h=e.type;e=e.fn;isUnDef(H[h])?H[h]=e:a.debug&&printErr("WARN: Transform '"+h+"' already exists.")}),isArray(g.output)&&g.output.forEach(e=>{var h=e.type;e=e.fn;M.has(h)?a.debug&&printErr("WARN: Output type '"+h+"' already exists."):M.set(h,e)}),isString(g.help)&&(G[b.toLowerCase()]=g.help))}else printErr("WARN: Library '"+b+"' does not have oafplib.")}}catch(e){printErr("WARN: Library '"+ diff --git a/js/openaf.js b/js/openaf.js index b46d40677..50e411c32 100644 --- a/js/openaf.js +++ b/js/openaf.js @@ -8486,20 +8486,20 @@ const $jsonrpc = function (aOptions) { const _defaultCmdDir = (isDef(__flags) && isDef(__flags.JSONRPC) && isDef(__flags.JSONRPC.cmd) && isDef(__flags.JSONRPC.cmd.defaultDir)) ? __flags.JSONRPC.cmd.defaultDir : __ - const _r = { - _ids: $atomic(1, "long"), - _p: __, - _s: false, - _cmd: false, - _sy: $sync(), - _q: {}, - _r: {}, - _info: __, - _pwd: _defaultCmdDir, - _copies: $atomic(0, "long"), - type: type => { - aOptions.type = type - return _r + const _r = { + _ids: $atomic(1, "long"), + _p: __, + _s: false, + _cmd: false, + _sy: $sync(), + _q: {}, + _r: {}, + _info: __, + _pwd: _defaultCmdDir, + _copies: $atomic(0, "long"), + type: type => { + aOptions.type = type + return _r }, url: url => { aOptions.url = url @@ -8538,7 +8538,7 @@ const $jsonrpc = function (aOptions) { $doWait($doAll( [ // in stream - $do(() => { + $doV(() => { $await("__jsonrpc_" + _main_id).notifyAll() do { var _id = _r._ids.get() @@ -8565,7 +8565,7 @@ const $jsonrpc = function (aOptions) { } while (!_r._s) }), // out stream - $do(() => { + $doV(() => { do { var _id = _r._ids.get() $await("__jsonrpc_r-" + _id + "-" + _main_id).wait() @@ -8608,13 +8608,13 @@ const $jsonrpc = function (aOptions) { if (isMap(aOptions.options.fns)) { if (isFunction(aOptions.options.fns[aMethod])) { var _res = aOptions.options.fns[aMethod](aParams) - _debug("jsonrpc dummy <- " + stringify({ result: _res }, __, "")) - if (aMethod == "initialize" && !aNotification) _r._info = _res - return _res - } else { - _debug("jsonrpc dummy <- " + stringify({ error: "Method not found" }, __, "")) - throw new Error("Method not found") - } + _debug("jsonrpc dummy <- " + stringify({ result: _res }, __, "")) + if (aMethod == "initialize" && !aNotification) _r._info = _res + return _res + } else { + _debug("jsonrpc dummy <- " + stringify({ error: "Method not found" }, __, "")) + throw new Error("Method not found") + } } return __ case "stdio": @@ -8644,12 +8644,12 @@ const $jsonrpc = function (aOptions) { } $await("__jsonrpc_a-" + _id + "-" + _main_id).wait(aOptions.timeout) }) - if (isMap(_r._r[_id])) { - _res = _r._r[_id] - delete _r._r[_id] - } - if (aMethod == "initialize" && !aNotification) _r._info = isDef(_res) && isDef(_res.result) ? _res.result : _res - return isDef(_res) && isDef(_res.result) ? _res.result : _res + if (isMap(_r._r[_id])) { + _res = _r._r[_id] + delete _r._r[_id] + } + if (aMethod == "initialize" && !aNotification) _r._info = isDef(_res) && isDef(_res.result) ? _res.result : _res + return isDef(_res) && isDef(_res.result) ? _res.result : _res case "remote": default: _$(aOptions.url, "aOptions.url").isString().$_() @@ -8672,23 +8672,23 @@ const $jsonrpc = function (aOptions) { } _debug("jsonrpc -> " + stringify(_req, __, "")) var res = $rest(_restOptions).post(aOptions.url, _req) - // Notifications do not expect a reply - if (!!aNotification) return - _debug("jsonrpc <- " + stringify(res, __, "")) - if (isDef(res)) { - if (isDef(res.error) && (isDef(res.error.response))) return res.error.response - if (aMethod == "initialize" && !aNotification) _r._info = res.result - if (isDef(res.result)) return res.result - } - return __ - } - }, - getInfo: () => _r._info, - destroy: () => { - if (_r._copies.get() > 0) { - _r._copies.dec() - return - } + // Notifications do not expect a reply + if (!!aNotification) return + _debug("jsonrpc <- " + stringify(res, __, "")) + if (isDef(res)) { + if (isDef(res.error) && (isDef(res.error.response))) return res.error.response + if (aMethod == "initialize" && !aNotification) _r._info = res.result + if (isDef(res.result)) return res.result + } + return __ + } + }, + getInfo: () => _r._info, + destroy: () => { + if (_r._copies.get() > 0) { + _r._copies.dec() + return + } _r._s = true if (isDef(_r._p)) { $doWait(_r._p) @@ -9000,26 +9000,26 @@ const $mcp = function(aOptions) { return _r }, initialize: (clientInfo) => { - clientInfo = _$(clientInfo, "clientInfo").isMap().default({}) - clientInfo = merge(aOptions.clientInfo, clientInfo) + clientInfo = _$(clientInfo, "clientInfo").isMap().default({}) + clientInfo = merge(aOptions.clientInfo, clientInfo) - var initResult = _jsonrpc.exec("initialize", { - protocolVersion: aOptions.protocolVersion, + var initResult = _jsonrpc.exec("initialize", { + protocolVersion: aOptions.protocolVersion, capabilities: { sampling: {} }, clientInfo: clientInfo }) - if (isDef(initResult) && isUnDef(initResult.error)) { - _r._initialized = true - _r._capabilities = initResult.capabilities || {} - _r._initResult = initResult + if (isDef(initResult) && isUnDef(initResult.error)) { + _r._initialized = true + _r._capabilities = initResult.capabilities || {} + _r._initResult = initResult - // Send initialized notification - if (aOptions.strict) { - try { - // send as a notification (no response expected) + // Send initialized notification + if (aOptions.strict) { + try { + // send as a notification (no response expected) _jsonrpc.exec("notifications/initialized", {}, true) } catch(e) { // Notifications might not return responses, ignore errors @@ -9028,15 +9028,15 @@ const $mcp = function(aOptions) { return _r } else { - throw new Error("MCP initialization failed: " + (isDef(initResult) ? initResult.error : __ || "Unknown error")) - } - }, - getInfo: () => _r._initResult, - listTools: () => { - if (!_r._initialized) { - throw new Error("MCP client not initialized. Call initialize() first.") - } - return _jsonrpc.exec("tools/list", {}) + throw new Error("MCP initialization failed: " + (isDef(initResult) ? initResult.error : __ || "Unknown error")) + } + }, + getInfo: () => _r._initResult, + listTools: () => { + if (!_r._initialized) { + throw new Error("MCP client not initialized. Call initialize() first.") + } + return _jsonrpc.exec("tools/list", {}) }, callTool: (toolName, toolArguments, toolOptions) => { if (!_r._initialized) { diff --git a/lib/jetty-io-12.1.6.jar b/lib/jetty-io-12.1.7.jar similarity index 81% rename from lib/jetty-io-12.1.6.jar rename to lib/jetty-io-12.1.7.jar index 0908a606d..78ab8e781 100644 Binary files a/lib/jetty-io-12.1.6.jar and b/lib/jetty-io-12.1.7.jar differ diff --git a/lib/jetty-util-12.1.6.jar b/lib/jetty-util-12.1.7.jar similarity index 90% rename from lib/jetty-util-12.1.6.jar rename to lib/jetty-util-12.1.7.jar index 2e6a559fa..3e849be35 100644 Binary files a/lib/jetty-util-12.1.6.jar and b/lib/jetty-util-12.1.7.jar differ diff --git a/pom.xml b/pom.xml index 8526ec5e1..a53c84adb 100644 --- a/pom.xml +++ b/pom.xml @@ -245,12 +245,12 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/ma org.eclipse.jetty jetty-io - 12.1.6 + 12.1.7 org.eclipse.jetty jetty-util - 12.1.6 + 12.1.7 org.eclipse.jetty.compression diff --git a/src/com/nwu/httpd/responses/FileResponse.java b/src/com/nwu/httpd/responses/FileResponse.java index f35df0af3..265702db4 100644 --- a/src/com/nwu/httpd/responses/FileResponse.java +++ b/src/com/nwu/httpd/responses/FileResponse.java @@ -22,10 +22,10 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; -import java.util.Map; -import java.util.Properties; -import java.util.StringTokenizer; +import java.net.URLEncoder; +import java.util.Map; +import java.util.Properties; +import java.util.StringTokenizer; import com.nwu.httpd.Codes; import com.nwu.httpd.HTTPd; @@ -79,71 +79,98 @@ public FileResponse(IHTTPd httpd, String rUri, Map props) { * Serves file from homeDir and its' subdirectories (only). Uses only URI, * ignores all headers and HTTP parameters. */ - public com.nwu.httpd.responses.Response serveFile(String uri, - Map header, File homeDir, boolean allowDirectoryListing) { - - uri = uri.replaceFirst(this.rURI, ""); - - // Make sure we won't die of an exception later - if (!homeDir.isDirectory()) - return new com.nwu.httpd.responses.SimpleResponse(httpd, - Codes.HTTP_INTERNALERROR, Codes.MIME_PLAINTEXT, - "INTERNAL ERRROR: serveFile(): given homeDir is not a directory."); - - // Remove URL arguments - uri = uri.trim().replace(File.separatorChar, '/'); - if (uri.indexOf('?') >= 0) - uri = uri.substring(0, uri.indexOf('?')); - - // Prohibit getting out of current directory - if (uri.startsWith("..") || uri.endsWith("..") - || uri.indexOf("../") >= 0) - return new com.nwu.httpd.responses.SimpleResponse(httpd, - Codes.HTTP_FORBIDDEN, Codes.MIME_PLAINTEXT, - "FORBIDDEN: Won't serve ../ for security reasons."); - - File f = new File(homeDir, uri); - if (!f.exists()) - return new com.nwu.httpd.responses.SimpleResponse(httpd, - Codes.HTTP_NOTFOUND, Codes.MIME_PLAINTEXT, - "Error 404, file not found."); + public com.nwu.httpd.responses.Response serveFile(String uri, + Map header, File homeDir, boolean allowDirectoryListing) { + String relativeUri = uri; + if (relativeUri == null) relativeUri = ""; + if (this.rURI != null && relativeUri.startsWith(this.rURI)) { + relativeUri = relativeUri.substring(this.rURI.length()); + } + + // Make sure we won't die of an exception later + if (!homeDir.isDirectory()) + return new com.nwu.httpd.responses.SimpleResponse(httpd, + Codes.HTTP_INTERNALERROR, Codes.MIME_PLAINTEXT, + "INTERNAL ERRROR: serveFile(): given homeDir is not a directory."); + + // Remove URL arguments + relativeUri = relativeUri.trim().replace('\\', '/'); + if (relativeUri.indexOf('?') >= 0) + relativeUri = relativeUri.substring(0, relativeUri.indexOf('?')); + while (relativeUri.startsWith("/")) relativeUri = relativeUri.substring(1); + if (relativeUri.indexOf('\0') >= 0) { + return new com.nwu.httpd.responses.SimpleResponse(httpd, + Codes.HTTP_FORBIDDEN, Codes.MIME_PLAINTEXT, + "FORBIDDEN: Invalid path."); + } + + // Prohibit getting out of current directory + if (relativeUri.startsWith("..") || relativeUri.endsWith("..") + || relativeUri.indexOf("../") >= 0) + return new com.nwu.httpd.responses.SimpleResponse(httpd, + Codes.HTTP_FORBIDDEN, Codes.MIME_PLAINTEXT, + "FORBIDDEN: Won't serve ../ for security reasons."); + + File f; + File baseDir; + try { + baseDir = homeDir.getCanonicalFile(); + f = relativeUri.isEmpty() ? baseDir : new File(baseDir, relativeUri).getCanonicalFile(); + String basePath = baseDir.getPath(); + String filePath = f.getPath(); + if (!filePath.equals(basePath) && !filePath.startsWith(basePath + File.separator)) { + return new com.nwu.httpd.responses.SimpleResponse(httpd, + Codes.HTTP_FORBIDDEN, Codes.MIME_PLAINTEXT, + "FORBIDDEN: Invalid path."); + } + } catch (IOException ioe) { + return new com.nwu.httpd.responses.SimpleResponse(httpd, + Codes.HTTP_FORBIDDEN, Codes.MIME_PLAINTEXT, + "FORBIDDEN: Invalid path."); + } + + String uriForLinks = relativeUri; + if (!f.exists()) + return new com.nwu.httpd.responses.SimpleResponse(httpd, + Codes.HTTP_NOTFOUND, Codes.MIME_PLAINTEXT, + "Error 404, file not found."); - // List the directory, if necessary - if (f.isDirectory()) { - // Browsers get confused without '/' after the - // directory, send a redirect. - if (!uri.endsWith("/")) { - uri += "/"; -// com.nwu.httpd.responses.Response r = new com.nwu.httpd.responses.SimpleResponse( -// httpd, Codes.HTTP_REDIRECT, Codes.MIME_HTML, -// ""+HTML_STYLE+"Redirected: " -// + this.rURI + uri + ""); -// r.addHeader("Location", this.rURI + uri); -// return r; - } - - this.rURI = this.rURI.replaceAll("/+", "/"); - uri = uri.replaceAll("/+", "/"); - - // First try index.html and index.htm - if (new File(f, "index.html").exists()) - f = new File(homeDir, uri + "/index.html"); - else if (new File(f, "index.htm").exists()) - f = new File(homeDir, uri + "/index.htm"); - // No index file, list the directory - else if (allowDirectoryListing) { - String[] files = f.list(); - String msg = ""+HTML_STYLE+"

Directory " + uri + "


"; - - if (uri.length() > 1) { - String u = uri.substring(0, uri.length() - 1); - int slash = u.lastIndexOf('/'); - if (slash >= 0 && slash < u.length()) - msg += "..
"; - } - - for (int i = 0; i < files.length; ++i) { + // List the directory, if necessary + if (f.isDirectory()) { + // Browsers get confused without '/' after the + // directory, send a redirect. + if (!uriForLinks.endsWith("/")) { + uriForLinks += "/"; +// com.nwu.httpd.responses.Response r = new com.nwu.httpd.responses.SimpleResponse( +// httpd, Codes.HTTP_REDIRECT, Codes.MIME_HTML, +// ""+HTML_STYLE+"Redirected: " +// + this.rURI + uri + ""); +// r.addHeader("Location", this.rURI + uri); +// return r; + } + + this.rURI = this.rURI.replaceAll("/+", "/"); + uriForLinks = uriForLinks.replaceAll("/+", "/"); + + // First try index.html and index.htm + if (new File(f, "index.html").exists()) + f = new File(f, "index.html"); + else if (new File(f, "index.htm").exists()) + f = new File(f, "index.htm"); + // No index file, list the directory + else if (allowDirectoryListing) { + String[] files = f.list(); + String msg = ""+HTML_STYLE+"

Directory /" + uriForLinks + "


"; + + if (uriForLinks.length() > 1) { + String u = uriForLinks.substring(0, uriForLinks.length() - 1); + int slash = u.lastIndexOf('/'); + if (slash >= 0 && slash < u.length()) + msg += "..
"; + } + + for (int i = 0; i < files.length; ++i) { File curFile = new File(f, files[i]); boolean dir = curFile.isDirectory(); if (dir) { @@ -151,8 +178,8 @@ else if (allowDirectoryListing) { files[i] += "/"; } - msg += "" - + files[i] + ""; + msg += "" + + files[i] + ""; // Show file size if (curFile.isFile()) { @@ -197,41 +224,35 @@ else if (len < 1024 * 1024) // Support (simple) skipping: long startFrom = 0; String range = header.get("range"); - if (range != null) { - if (range.startsWith("bytes=")) { - range = range.substring("bytes=".length()); - int minus = range.indexOf('-'); - if (minus > 0) - range = range.substring(0, minus); - try { - startFrom = Long.parseLong(range); - } catch (NumberFormatException nfe) { - } - } - } - - FileInputStream fis = null; - try { - fis = new FileInputStream(f); - fis.skip(startFrom); - com.nwu.httpd.responses.Response r = new com.nwu.httpd.responses.SimpleResponse( - httpd, Codes.HTTP_OK, mime, fis); - r.addHeader("Content-length", "" + (f.length() - startFrom)); - r.addHeader("Content-range", "" + startFrom + "-" - + (f.length() - 1) + "/" + f.length()); - return r; - } finally { - if (fis != null) { - try { - fis.close(); - } catch (IOException e) { - // Log the exception or handle it as needed - } - } - } - } catch (IOException ioe) { - return new com.nwu.httpd.responses.SimpleResponse(httpd, - Codes.HTTP_FORBIDDEN, Codes.MIME_PLAINTEXT, + if (range != null) { + if (range.startsWith("bytes=")) { + range = range.substring("bytes=".length()); + int minus = range.indexOf('-'); + if (minus > 0) + range = range.substring(0, minus); + try { + startFrom = Long.parseLong(range); + } catch (NumberFormatException nfe) { + } + } + } + if (startFrom < 0 || startFrom >= f.length()) { + return new com.nwu.httpd.responses.SimpleResponse(httpd, + com.nwu.httpd.NanoHTTPD.Response.Status.RANGE_NOT_SATISFIABLE, Codes.MIME_PLAINTEXT, + "Requested Range Not Satisfiable"); + } + + FileInputStream fis = new FileInputStream(f); + fis.skip(startFrom); + com.nwu.httpd.responses.Response r = new com.nwu.httpd.responses.SimpleResponse( + httpd, Codes.HTTP_OK, mime, fis); + r.addHeader("Content-length", "" + (f.length() - startFrom)); + r.addHeader("Content-range", "" + startFrom + "-" + + (f.length() - 1) + "/" + f.length()); + return r; + } catch (IOException ioe) { + return new com.nwu.httpd.responses.SimpleResponse(httpd, + Codes.HTTP_FORBIDDEN, Codes.MIME_PLAINTEXT, "FORBIDDEN: Reading file failed."); } } diff --git a/src/com/nwu2/httpd/responses/FileResponse.java b/src/com/nwu2/httpd/responses/FileResponse.java index 82c57141a..e389b7bca 100644 --- a/src/com/nwu2/httpd/responses/FileResponse.java +++ b/src/com/nwu2/httpd/responses/FileResponse.java @@ -81,8 +81,11 @@ public FileResponse(IHTTPd httpd, String rUri, Map props) { */ public com.nwu2.httpd.responses.Response serveFile(String uri, Map header, File homeDir, boolean allowDirectoryListing) { - - uri = uri.replaceFirst(this.rURI, ""); + String relativeUri = uri; + if (relativeUri == null) relativeUri = ""; + if (this.rURI != null && relativeUri.startsWith(this.rURI)) { + relativeUri = relativeUri.substring(this.rURI.length()); + } // Make sure we won't die of an exception later if (!homeDir.isDirectory()) @@ -91,59 +94,84 @@ public com.nwu2.httpd.responses.Response serveFile(String uri, "INTERNAL ERRROR: serveFile(): given homeDir is not a directory."); // Remove URL arguments - uri = uri.trim().replace(File.separatorChar, '/'); - if (uri.indexOf('?') >= 0) - uri = uri.substring(0, uri.indexOf('?')); + relativeUri = relativeUri.trim().replace('\\', '/'); + if (relativeUri.indexOf('?') >= 0) + relativeUri = relativeUri.substring(0, relativeUri.indexOf('?')); + while (relativeUri.startsWith("/")) relativeUri = relativeUri.substring(1); + if (relativeUri.indexOf('\0') >= 0) { + return new com.nwu2.httpd.responses.SimpleResponse(httpd, + Codes.HTTP_FORBIDDEN, Codes.MIME_PLAINTEXT, + "FORBIDDEN: Invalid path."); + } // Prohibit getting out of current directory - if (uri.startsWith("..") || uri.endsWith("..") - || uri.indexOf("../") >= 0) + if (relativeUri.startsWith("..") || relativeUri.endsWith("..") + || relativeUri.indexOf("../") >= 0 + || relativeUri.indexOf(".." + File.separator) >= 0) return new com.nwu2.httpd.responses.SimpleResponse(httpd, Codes.HTTP_FORBIDDEN, Codes.MIME_PLAINTEXT, "FORBIDDEN: Won't serve ../ for security reasons."); - File f = new File(homeDir, uri); + File f; + File baseDir; + try { + baseDir = homeDir.getCanonicalFile(); + f = relativeUri.isEmpty() ? baseDir : new File(baseDir, relativeUri).getCanonicalFile(); + String basePath = baseDir.getPath(); + String filePath = f.getPath(); + if (!filePath.equals(basePath) && !filePath.startsWith(basePath + File.separator)) { + return new com.nwu2.httpd.responses.SimpleResponse(httpd, + Codes.HTTP_FORBIDDEN, Codes.MIME_PLAINTEXT, + "FORBIDDEN: Invalid path."); + } + } catch (IOException ioe) { + return new com.nwu2.httpd.responses.SimpleResponse(httpd, + Codes.HTTP_FORBIDDEN, Codes.MIME_PLAINTEXT, + "FORBIDDEN: Invalid path."); + } + + String uriForLinks = relativeUri; if (!f.exists()) return new com.nwu2.httpd.responses.SimpleResponse(httpd, Codes.HTTP_NOTFOUND, Codes.MIME_PLAINTEXT, "Error 404, file not found."); - // List the directory, if necessary - if (f.isDirectory()) { - // Browsers get confused without '/' after the - // directory, send a redirect. - if (!uri.endsWith("/")) { - uri += "/"; + // List the directory, if necessary + if (f.isDirectory()) { + // Browsers get confused without '/' after the + // directory, send a redirect. + if (!uriForLinks.endsWith("/")) { + uriForLinks += "/"; // com.nwu2.httpd.responses.Response r = new com.nwu2.httpd.responses.SimpleResponse( // httpd, Codes.HTTP_REDIRECT, Codes.MIME_HTML, // ""+HTML_STYLE+"Redirected: " // + this.rURI + uri + ""); // r.addHeader("Location", this.rURI + uri); // return r; - } + } - this.rURI = this.rURI.replaceAll("/+", "/"); - uri = uri.replaceAll("/+", "/"); - - // First try index.html and index.htm - if (new File(f, "index.html").exists()) - f = new File(homeDir, uri + "/index.html"); - else if (new File(f, "index.htm").exists()) - f = new File(homeDir, uri + "/index.htm"); - // No index file, list the directory - else if (allowDirectoryListing) { - String[] files = f.list(); - String msg = ""+HTML_STYLE+"

Directory " + uri + "


"; + this.rURI = this.rURI.replaceAll("/+", "/"); + uriForLinks = uriForLinks.replaceAll("/+", "/"); + + // First try index.html and index.htm + if (new File(f, "index.html").exists()) + f = new File(f, "index.html"); + else if (new File(f, "index.htm").exists()) + f = new File(f, "index.htm"); + // No index file, list the directory + else if (allowDirectoryListing) { + String[] files = f.list(); + String msg = ""+HTML_STYLE+"

Directory /" + uriForLinks + "


"; - if (uri.length() > 1) { - String u = uri.substring(0, uri.length() - 1); - int slash = u.lastIndexOf('/'); - if (slash >= 0 && slash < u.length()) - msg += "..
"; - } + if (uriForLinks.length() > 1) { + String u = uriForLinks.substring(0, uriForLinks.length() - 1); + int slash = u.lastIndexOf('/'); + if (slash >= 0 && slash < u.length()) + msg += "..
"; + } - for (int i = 0; i < files.length; ++i) { + for (int i = 0; i < files.length; ++i) { File curFile = new File(f, files[i]); boolean dir = curFile.isDirectory(); if (dir) { @@ -151,8 +179,8 @@ else if (allowDirectoryListing) { files[i] += "/"; } - msg += "" - + files[i] + ""; + msg += "" + + files[i] + ""; // Show file size if (curFile.isFile()) { @@ -197,41 +225,48 @@ else if (len < 1024 * 1024) // Support (simple) skipping: long startFrom = 0; String range = header.get("range"); - if (range != null) { - if (range.startsWith("bytes=")) { - range = range.substring("bytes=".length()); - int minus = range.indexOf('-'); - if (minus > 0) - range = range.substring(0, minus); - try { - startFrom = Long.parseLong(range); - } catch (NumberFormatException nfe) { + if (range != null) { + if (range.startsWith("bytes=")) { + range = range.substring("bytes=".length()); + int minus = range.indexOf('-'); + if (minus > 0) + range = range.substring(0, minus); + try { + startFrom = Long.parseLong(range); + } catch (NumberFormatException nfe) { + } } } - } + if (startFrom < 0 || startFrom >= f.length()) { + return new com.nwu2.httpd.responses.SimpleResponse(httpd, + com.nwu2.httpd.NanoHTTPD.Response.Status.RANGE_NOT_SATISFIABLE, Codes.MIME_PLAINTEXT, + "Requested Range Not Satisfiable"); + } - FileInputStream fis = null; - try { - fis = new FileInputStream(f); - fis.skip(startFrom); - com.nwu2.httpd.responses.Response r = new com.nwu2.httpd.responses.SimpleResponse( - httpd, Codes.HTTP_OK, mime, fis); - r.addHeader("Content-length", "" + (f.length() - startFrom)); - r.addHeader("Content-range", "" + startFrom + "-" - + (f.length() - 1) + "/" + f.length()); - return r; - } finally { - if (fis != null) { - try { - fis.close(); - } catch (IOException e) { - // Log the exception or handle it as needed + FileInputStream fis = null; + boolean success = false; + try { + fis = new FileInputStream(f); + fis.skip(startFrom); + com.nwu2.httpd.responses.Response r = new com.nwu2.httpd.responses.SimpleResponse( + httpd, Codes.HTTP_OK, mime, fis); + r.addHeader("Content-length", "" + (f.length() - startFrom)); + r.addHeader("Content-range", "" + startFrom + "-" + + (f.length() - 1) + "/" + f.length()); + success = true; + return r; + } finally { + if (!success && fis != null) { + try { + fis.close(); + } catch (IOException e) { + // Ignored: best-effort to close stream on failure + } } } - } - } catch (IOException ioe) { - return new com.nwu2.httpd.responses.SimpleResponse(httpd, - Codes.HTTP_FORBIDDEN, Codes.MIME_PLAINTEXT, + } catch (IOException ioe) { + return new com.nwu2.httpd.responses.SimpleResponse(httpd, + Codes.HTTP_FORBIDDEN, Codes.MIME_PLAINTEXT, "FORBIDDEN: Reading file failed."); } }