import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.MalformedURLException; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.Semaphore; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.json.JSONException; import org.json.JSONObject; import org.pircbotx.Channel; import org.pircbotx.PircBotX; import org.pircbotx.User; import pl.shockah.HTTPQuery; import pl.shockah.Helper; import pl.shockah.shocky.Cache; import pl.shockah.shocky.Data; import pl.shockah.shocky.ScriptModule; import pl.shockah.shocky.cmds.Command; import pl.shockah.shocky.cmds.Parameters; import pl.shockah.shocky.interfaces.IFactoidData; import pl.shockah.shocky.sql.Factoid; public class ModulePHP extends ScriptModule implements IFactoidData { public static final File savedData = new File("data", "php").getAbsoluteFile(); public Map<Long,Semaphore> locks = new HashMap<Long,Semaphore>(); protected Command cmd; public String name() {return "php";} public String identifier() {return "php";} public char stringCharacter() {return '\'';} public void onEnable(File dir) { if (!savedData.exists()) savedData.mkdirs(); if (!Data.config.exists("php-version") || Data.config.getInt("php-version") < 2) { Data.config.set("php-version", 2); Pattern pattern = Pattern.compile("^(.+?)_(-?[0-9]+)$"); for (File f : savedData.listFiles()) { Matcher m = pattern.matcher(f.getName()); if (!m.find()) continue; int hash; try { hash = Integer.parseInt(m.group(2)); } catch(Throwable t) { continue; } f.renameTo(new File(f.getParentFile(),String.format("%s_%08X",m.group(1),hash))); } } Data.config.setNotExists("php-url","http://localhost/shocky/shocky.php"); Command.addCommands(this, cmd = new CmdPHP()); Data.protectedKeys.add("php-url"); } public void onDisable() { Command.removeCommands(cmd); } public File[] getReadableFiles() { return new File[]{savedData}; } public String parse(Cache cache, PircBotX bot, Channel channel, User sender, Factoid factoid, String code, String message) { if (code == null) return ""; HTTPQuery q; try { q = HTTPQuery.create(Data.forChannel(channel).getString("php-url"),HTTPQuery.Method.POST); } catch (MalformedURLException e1) { return "php-url is invalid"; } String ret = null; try { StringBuilder sb = new StringBuilder(); buildInit(sb,getParams(bot, channel, sender, message, factoid).entrySet()); String data = getData(factoid, true); if (data != null) { sb.append("$_STATE=json_decode("); appendEscape(sb,data); sb.append(");"); } sb.append(code); q.connect(true,true); q.write(HTTPQuery.parseArgs("code",sb.toString())); ret = q.readWhole(); q.close(); JSONObject json = new JSONObject(ret); JSONObject error = json.optJSONObject("error"); if (error != null) return error.getString("message"); String safe_errors = json.optString("safe_errors", null); if (safe_errors != null) return safe_errors; String newdata = json.optString("data", null); if (factoid != null && newdata != null && (data == null || !newdata.contentEquals(data))) setData(factoid, newdata); return json.optString("output"); } catch (JSONException e) { } catch (Exception e) { e.printStackTrace(); } finally { release(factoid); } return (ret != null)?ret.trim():null; } private void buildInit(StringBuilder sb, Iterable<Map.Entry<String,Object>> set) { for (Map.Entry<String,Object> pair : set) { sb.append('$').append(pair.getKey()).append('='); appendObject(sb, pair.getValue()); sb.append(';'); } } private void appendObject(StringBuilder sb, Object obj) { if (obj == null) { sb.append("null"); } else if (obj.getClass().isArray()) { Object[] a = (Object[])obj; sb.append("array("); for (int i = 0; i < a.length; ++i) { if (i > 0) sb.append(','); appendObject(sb, a[i]); } sb.append(')'); } else if (obj instanceof Map) { sb.append("array("); int i = 0; for (Entry<?,?> entry : ((Map<?,?>)obj).entrySet()) { if (i++ > 0) sb.append(','); appendObject(sb, entry.getKey()); sb.append("=>"); appendObject(sb, entry.getValue()); } sb.append(')'); } else if (obj instanceof String) { appendEscape(sb,(String)obj); } else if (obj instanceof Number) { sb.append(obj.toString()); } } public String readFile(File file) { if (file == null) return null; try { FileInputStream fs = new FileInputStream(file); InputStreamReader sr = new InputStreamReader(fs, Helper.utf8); char[] buffer = new char[(int)file.length()]; sr.read(buffer); sr.close(); return new String(buffer); } catch (IOException e) { e.printStackTrace(); } return null; } public boolean writeFile(File file, CharSequence str) { if (file == null) return false; try { if (str == null || str.length() == 0) return file.delete(); FileOutputStream fs = new FileOutputStream(file); OutputStreamWriter sr = new OutputStreamWriter(fs, Helper.utf8); sr.append(str); sr.close(); return true; } catch (IOException e) { e.printStackTrace(); } return false; } public File getFactoidSave(Factoid f) { if (f != null) return new File(savedData,String.format("%s_%08X", f.channel== null? "global" : f.channel, f.name.hashCode())); return null; } @Override public String getData(Factoid f, boolean acquire) { File saveFile = getFactoidSave(f); if (saveFile != null && saveFile.exists() && saveFile.canRead()) { if (acquire) { Semaphore lock; synchronized(locks) { if (!locks.containsKey(f.id)) locks.put(f.id, new Semaphore(1)); lock = locks.get(f.id); } try { lock.acquire(); } catch (InterruptedException e) { return null; } } return readFile(saveFile); } return null; } @Override public boolean setData(Factoid f, CharSequence data) { if (f == null) return false; try { if (data != null && data.length() < (1024 * 10)) { File saveFile = getFactoidSave(f); if (saveFile.exists() || saveFile.createNewFile()) return writeFile(saveFile,data); } } catch (IOException e) { e.printStackTrace(); } return false; } public void release(Factoid f) { if (f == null) return; synchronized(locks) { if (locks.containsKey(f.id)) { Semaphore lock = locks.get(f.id); lock.release(); if (!lock.hasQueuedThreads()) locks.remove(f.id); } } } public class CmdPHP extends ScriptCommand { public String command() {return "php";} public String help(Parameters params) { return "\nphp {code} - runs PHP code"; } } }