PageRenderTime 21ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 1ms

/src/com/dercd/cdio/CDProtocol.java

https://gitlab.com/Moylle/CDAPI
Java | 481 lines | 433 code | 34 blank | 14 comment | 72 complexity | 57a9d88966ed18a5f42c939e6df291db MD5 | raw file
  1. package com.dercd.cdio;
  2. import java.io.IOException;
  3. import java.io.InputStream;
  4. import java.io.OutputStream;
  5. import java.io.PrintWriter;
  6. import java.io.StringWriter;
  7. import java.net.Socket;
  8. import java.nio.charset.Charset;
  9. import java.util.function.Consumer;
  10. public class CDProtocol
  11. {
  12. public AfterVerificationDelegate afterVerification;
  13. Socket socket;
  14. InputStream inputStream;
  15. OutputStream outputStream;
  16. private String keyWord;
  17. private static final int keyWordLength = 32;
  18. private int maxLength;
  19. protected DataInputObject dio;
  20. protected RawDataInputObject dioRaw;
  21. private boolean verifyed = false;
  22. private int length = -1;
  23. private int processed = 0;
  24. public boolean useBigEndian = true;
  25. public Charset encoding = Charset.forName("UTF-8");
  26. protected boolean rawGothrough;
  27. private final byte[] ringBuffer;
  28. protected int buf_start;
  29. protected int buf_current;
  30. protected final int buf_capacity;
  31. protected boolean receivingRaw = false;
  32. protected final boolean[] currentOptions = new boolean[8];
  33. public Consumer<String> out;
  34. public static Charset ascii = Charset.forName("ASCII");
  35. public CDProtocol(Socket socket, int maxLength, int buf_capacity, boolean incomming) throws Exception
  36. {
  37. this(socket, maxLength, buf_capacity, incomming, null, null, null, true);
  38. }
  39. public CDProtocol(Socket socket, int maxLength, int buf_capacity, boolean incomming, String hashedKeyWord, DataInputObject dio, RawDataInputObject dioRaw, boolean useBigEndian) throws Exception
  40. {
  41. this.socket = socket;
  42. this.inputStream = socket.getInputStream();
  43. this.outputStream = socket.getOutputStream();
  44. setKeyword(hashedKeyWord);
  45. if (buf_capacity < keyWordLength)
  46. throw new Exception("Buffer have to be at least " + keyWordLength + " bytes long (keyword length)");
  47. this.buf_capacity = buf_capacity;
  48. this.ringBuffer = new byte[buf_capacity];
  49. setMaxLength(maxLength);
  50. setDIO(dio);
  51. setDIORaw(dioRaw);
  52. }
  53. public synchronized void process(byte[] data, int length) throws IOException
  54. {
  55. int overstrip = addRange(data, length);
  56. if (!this.verifyed)
  57. {
  58. if (!checkIdentification())
  59. {
  60. println("Identification failed");
  61. close();
  62. throw new IOException("Identification failed");
  63. }
  64. if (this.verifyed)
  65. {
  66. println("Identification successfull");
  67. if(this.afterVerification != null)
  68. this.afterVerification.afterVerification(this);
  69. }
  70. else
  71. return;
  72. }
  73. boolean toProcess = true;
  74. while ((toProcess && this.buf_current != this.buf_start))
  75. {
  76. if (this.length == -1)
  77. {
  78. if (currentLength() < 5)
  79. return;
  80. resetOptions();
  81. int l = 0;
  82. int bufStart;
  83. if (this.useBigEndian)
  84. for (int i = 0; i < 4; ++i)
  85. {
  86. bufStart = getBufStart();
  87. //DEBUG
  88. //System.out.println("Lenght byte: " + (this.ringBuffer[bufStart]) + ", Converted: " + (this.ringBuffer[bufStart] & 0xFF));
  89. l = (l << 8) | (this.ringBuffer[bufStart] & 0xFF);
  90. setBufStart(bufStart + 1);
  91. }
  92. else
  93. for (int i = 0; i < 4; ++i)
  94. {
  95. bufStart = getBufStart();
  96. l = (l >> 8) | (this.ringBuffer[bufStart] & 0xFF);
  97. setBufStart(bufStart + 1);
  98. }
  99. //DEBUG
  100. //System.out.println("Received len: " + l);
  101. if (l <= 0)
  102. throw new IOException("CDProtocol: Length out of bounds (" + l + "). Terminating connection");
  103. if (l > this.maxLength)
  104. throw new IOException("CDProtocol: Length over " + this.maxLength + " chars (" + l + "). Terminating connection");
  105. this.length = l;
  106. extractOptions();
  107. }
  108. if (this.currentOptions[0])
  109. if (this.rawGothrough)
  110. toProcess = procReadyRawGT();
  111. else
  112. toProcess = procReadyEntire(true);
  113. else
  114. toProcess = procReadyEntire(false);
  115. if (overstrip != 0)
  116. {
  117. addRange(data, overstrip, length - overstrip);
  118. toProcess = true;
  119. }
  120. }
  121. }
  122. protected int addRange(byte[] input, int length)
  123. {
  124. return addRange(input, length, 0);
  125. }
  126. protected int addRange(byte[] input, int length, int start)
  127. {
  128. int overstrip = 0;
  129. if (currentLength() + length >= this.buf_capacity)
  130. {
  131. overstrip = length - (this.buf_capacity - 1 - currentLength());
  132. length -= overstrip;
  133. }
  134. int bufCurrent;
  135. for (int i = 0; i < length; ++i)
  136. {
  137. bufCurrent = getBufCurrent();
  138. //DEBUG
  139. //System.out.println("Buf: char " + ((int) input[i]) + ", byte " + ((byte) input[i]));
  140. this.ringBuffer[bufCurrent] = input[i];
  141. setBufCurrent(bufCurrent + 1);
  142. }
  143. return overstrip;
  144. }
  145. protected void extractOptions()
  146. {
  147. int bufStart = getBufStart();
  148. byte b = this.ringBuffer[bufStart];
  149. setBufStart(bufStart + 1);
  150. if ((b & OptionMask.RAW.getValue()) == 1)
  151. this.currentOptions[0] = true;
  152. }
  153. protected void resetOptions()
  154. {
  155. for (int i = this.currentOptions.length - 1; i >= 0; --i)
  156. this.currentOptions[i] = false;
  157. }
  158. protected byte[] createConnectedArray()
  159. {
  160. return createConnectedArray(-1);
  161. }
  162. protected byte[] createConnectedArray(int length)
  163. {
  164. if (length <= 0)
  165. length = currentLength();
  166. byte[] b = new byte[length];
  167. int j = this.buf_start, i = 0;
  168. for (; i < length && j < this.buf_capacity; ++i, ++j)
  169. b[i] = this.ringBuffer[j];
  170. j = 0;
  171. for (; i < length; ++i, ++j)
  172. b[i] = this.ringBuffer[j];
  173. return b;
  174. }
  175. protected void callDIO(byte[] data, int start, int length, boolean raw)
  176. {
  177. if (raw)
  178. this.dioRaw.input(data, true, start, length);
  179. else
  180. this.dio.input(new String(data, start, length, this.encoding));
  181. }
  182. protected boolean procReadyEntire(boolean raw)
  183. {
  184. int bufCount = currentLength();
  185. if (bufCount < this.length)
  186. return false;
  187. if (this.buf_current > this.buf_start || this.buf_start + this.length < this.buf_capacity)
  188. callDIO(this.ringBuffer, this.buf_start, this.length, raw);
  189. else
  190. callDIO(createConnectedArray(bufCount == this.length ? -1 : this.length), 0, this.length, raw);
  191. if (bufCount == this.length)
  192. this.buf_start = this.buf_current;
  193. else
  194. setBufStart(getBufStart() + this.length);
  195. this.length = -1;
  196. this.notifyAll();
  197. return true;
  198. }
  199. protected boolean procReadyRawGT()
  200. {
  201. int toProcess = this.length - this.processed, bufCount = currentLength();
  202. if (toProcess <= bufCount)
  203. {
  204. this.processed = 0;
  205. this.length = -1;
  206. if (toProcess == bufCount)
  207. {
  208. if (isOverlap())
  209. this.dioRaw.input(createConnectedArray(), true, 0, toProcess);
  210. else
  211. this.dioRaw.input(this.ringBuffer, true, this.buf_start, bufCount);
  212. this.buf_start = this.buf_current;
  213. }
  214. else
  215. {
  216. if (this.buf_start + toProcess >= this.buf_capacity)
  217. this.dioRaw.input(createConnectedArray(toProcess), true, 0, toProcess);
  218. else
  219. this.dioRaw.input(this.ringBuffer, true, this.buf_start, toProcess);
  220. setBufStart(getBufStart() + toProcess);
  221. }
  222. this.notifyAll();
  223. }
  224. else
  225. {
  226. if (isOverlap())
  227. this.dioRaw.input(createConnectedArray(), false, 0, bufCount);
  228. else
  229. this.dioRaw.input(this.ringBuffer, false, this.buf_start, bufCount);
  230. this.buf_start = this.buf_current;
  231. this.processed += bufCount;
  232. }
  233. return true;
  234. }
  235. private boolean checkIdentification()
  236. {
  237. try
  238. {
  239. if (currentLength() < keyWordLength)
  240. return true;
  241. String s = new String(this.ringBuffer, this.buf_start, keyWordLength, ascii);
  242. if (!s.startsWith(this.keyWord))
  243. return false;
  244. setBufStart(getBufStart() + keyWordLength);
  245. this.verifyed = true;
  246. return true;
  247. }
  248. catch (Exception x)
  249. {}
  250. return false;
  251. }
  252. public boolean isAlive()
  253. {
  254. return this.socket != null && this.socket.isConnected();
  255. }
  256. public void send(String s)
  257. {
  258. try
  259. {
  260. //DEBUG
  261. //System.out.println("Sending: '" + s + "'");
  262. sendRaw(s.getBytes(this.encoding));
  263. }
  264. catch (Throwable t)
  265. {
  266. printException(t);
  267. close();
  268. }
  269. }
  270. public void sendRaw(byte[] b, OptionMask... options)
  271. {
  272. sendRaw(b, true, 0, -1, options);
  273. }
  274. public void sendRaw(byte[] b, boolean addHeader, OptionMask... options)
  275. {
  276. sendRaw(b, addHeader, 0, -1, options);
  277. }
  278. public void sendRaw(byte[] b, int start, OptionMask... options)
  279. {
  280. sendRaw(b, true, start, -1, options);
  281. }
  282. public void sendRaw(byte[] b, boolean addHeader, int start, OptionMask... options)
  283. {
  284. sendRaw(b, addHeader, start, -1, options);
  285. }
  286. public void sendRaw(byte[] b, int start, int length, OptionMask... options)
  287. {
  288. sendRaw(b, true, start, length, options);
  289. }
  290. public void sendRaw(byte[] b, boolean addHeader, int start, int length, OptionMask... options)
  291. {
  292. try
  293. {
  294. //DEBUG
  295. //System.out.println("Alive: " + isAlive());
  296. //System.out.println("Verified: " + this.verifyed);
  297. if (!isAlive() || !this.verifyed)
  298. return;
  299. if (length == -1)
  300. length = b.length;
  301. //DEBUG
  302. //System.out.println("Length: " + length);
  303. //System.out.println("Real lenght: " + b.length);
  304. synchronized (this.socket)
  305. {
  306. if (addHeader)
  307. {
  308. if (this.useBigEndian)
  309. for (int i = 3; i >= 0; i--)
  310. this.outputStream.write(length >> (8 * i));
  311. else
  312. for (int i = 0; i < 4; i++)
  313. this.outputStream.write(length >> (8 * i));
  314. int optionByte = 0;
  315. for (OptionMask om : options)
  316. optionByte |= om.getValue();
  317. this.outputStream.write(optionByte);
  318. }
  319. this.outputStream.write(b, start, length);
  320. this.outputStream.flush();
  321. }
  322. }
  323. catch (Throwable t)
  324. {
  325. printException(t);
  326. close();
  327. }
  328. }
  329. public void close()
  330. {
  331. try
  332. {
  333. this.socket.close();
  334. }
  335. catch (Exception x)
  336. {}
  337. println("Closed");
  338. }
  339. public boolean isVerifyed()
  340. {
  341. return this.verifyed;
  342. }
  343. public int getMaxLength()
  344. {
  345. return this.maxLength;
  346. }
  347. public void setMaxLength(int value) throws Exception
  348. {
  349. if (value > this.ringBuffer.length)
  350. throw new Exception("MaxLength must not be higher than buf_capacity");
  351. this.maxLength = value;
  352. }
  353. private int getBufCurrent()
  354. {
  355. return this.buf_current;
  356. }
  357. private void setBufCurrent(int value)
  358. {
  359. this.buf_current = value >= this.buf_capacity ? value - this.buf_capacity : value;
  360. }
  361. private int getBufStart()
  362. {
  363. return this.buf_start;
  364. }
  365. private void setBufStart(int value)
  366. {
  367. this.buf_start = value >= this.buf_capacity ? value - this.buf_capacity : value;
  368. }
  369. public void setKeyword(byte[] keyWord)
  370. {
  371. this.keyWord = CDConnection.hash(keyWord != null ? keyWord : new byte[0]);
  372. }
  373. public void setKeyword(String hashedKeyWord) throws Exception
  374. {
  375. if(hashedKeyWord == null)
  376. setKeyword(new byte[0]);
  377. else if(hashedKeyWord.length() != keyWordLength)
  378. throw new Exception("Hashed keyword must have a length of " + keyWordLength + " (keyWordLength)");
  379. this.keyWord = hashedKeyWord;
  380. }
  381. protected int currentLength()
  382. {
  383. int length = this.buf_current - this.buf_start;
  384. return length < 0 ? length + this.buf_capacity : length;
  385. }
  386. protected boolean isOverlap()
  387. {
  388. return this.buf_current < this.buf_start;
  389. }
  390. public void setDIO(DataInputObject value)
  391. {
  392. this.dio = value != null ? value : (input) -> {};
  393. }
  394. public void setDIORaw(RawDataInputObject value)
  395. {
  396. this.dioRaw = value != null ? value : (input, end, start, length) -> {};
  397. }
  398. public boolean getRawGothrough()
  399. {
  400. return this.rawGothrough;
  401. }
  402. public void setRawGothrough(boolean rawGothrough) throws InterruptedException
  403. {
  404. synchronized (this)
  405. {
  406. if (this.length != -1)
  407. this.wait();
  408. this.rawGothrough = rawGothrough;
  409. }
  410. }
  411. public void setRawGothroughAsync(boolean rawGothrough)
  412. {
  413. new Thread(() -> {
  414. try
  415. {
  416. setRawGothrough(rawGothrough);
  417. }
  418. catch (Exception x)
  419. {
  420. x.printStackTrace();
  421. }
  422. }).start();
  423. }
  424. public void resetVerification()
  425. {
  426. this.verifyed = false;
  427. }
  428. protected void println(String s)
  429. {
  430. printlnRaw("CDP: " + s);
  431. }
  432. protected void printlnRaw(String s)
  433. {
  434. if(this.out != null)
  435. this.out.accept(s);
  436. }
  437. protected void printException(Throwable t)
  438. {
  439. if(this.out != null)
  440. {
  441. StringWriter w = new StringWriter();
  442. t.printStackTrace(new PrintWriter(w));
  443. for(String line : w.toString().split("\\r?\\n|\\r"))
  444. this.out.accept(line);
  445. }
  446. }
  447. }