Advertisement
Sorunome

C++ IRC framework

Aug 27th, 2013
153
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 16.96 KB | None | 0 0
  1. /***** filename: irc.h ******/
  2. #ifndef IRC_H
  3. #define IRC_H
  4. #include <string>
  5. #include "md5.h"
  6. void handleMsgActions(std::string message,std::string action,std::string nickName,std::string ident,std::string command,std::string args);
  7. void handleNormalActions(std::string message,std::string action,std::string nickName,std::string ident,std::string command,std::string args,std::string postChannel,bool omnom);
  8. std::string toLower(std::string string) {
  9. transform(string.begin(), string.end(), string.begin(), ::tolower);
  10. return string;
  11. }
  12. class irc {
  13. public:
  14. std::string server;
  15. std::string port;
  16. std::string nick;
  17. std::string channel;
  18. std::string sbuf;
  19. std::string message;
  20. std::string nickName;
  21. std::string command;
  22. std::string ident;
  23. std::string args;
  24. std::string postChannel;
  25. std::string action;
  26. std::string rbuf;
  27. std::string passwd;
  28. bool omnom;
  29. char charTemp[512];
  30. int sock;
  31. struct addrinfo *address;
  32. void sendSbuf() {
  33. send(sock,sbuf.c_str(),sbuf.size(),0);
  34. }
  35.  
  36. void constructAddress() {
  37. int gai_err = getaddrinfo(server.c_str(),port.c_str(), NULL, &address);
  38. std::cout << "gai_err (IRC): " << gai_err << " " << gai_strerror(gai_err) << std::endl;
  39. }
  40. void openSocket() {
  41. sock = socket(address->ai_family, address->ai_socktype, address->ai_protocol);
  42. connect(sock, address->ai_addr, address->ai_addrlen);
  43. }
  44. void closeSocket() {
  45. close(sock);
  46. }
  47. void setNick() {
  48. sbuf = "NICK " + nick + "\nUSER " + nick + " 0 * :" + nick + "\r\n";
  49. sendSbuf();
  50. }
  51. void joinChannel(std::string chan) {
  52. sbuf = "JOIN " + chan + "\n";
  53. sendSbuf();
  54. }
  55. irc(std::string serverb,std::string nickb,std::string channelb,std::string passwdb) {
  56. passwd = passwdb;
  57. server = serverb;
  58. port = "6667";
  59. nick = nickb;
  60. channel = channelb;
  61. constructAddress();
  62. openSocket();
  63. setNick();
  64. joinChannel(channel);
  65. }
  66. void rejoin() {
  67. closeSocket();
  68. openSocket();
  69. setNick();
  70. joinChannel(channel);;
  71. }
  72.  
  73. void handleQuitMessage() {
  74. if (action == "QUIT" && nickName == nick)
  75. rejoin();
  76. }
  77. void handleMsgCommands() {
  78. if (command=="goadmin") {
  79. command = args.substr(0,args.find(" "));
  80. args = args.substr(args.find(" ")+1);
  81. if (md5(command)==passwd) {
  82. command = args.substr(0,args.find(" "));
  83. args = args.substr(args.find(" ")+1);
  84. if (command=="join")
  85. sbuf = "JOIN " + args + "\n";
  86. if (command=="part")
  87. sbuf = "PART " + args + "\n";
  88. if (command=="tell")
  89. sbuf = "PRIVMSG " + args.substr(0,args.find(" ")) + " :" + args.substr(args.find(" ")+1) + "\n";
  90. if (command=="raw")
  91. sbuf = args + "\n";
  92. } else
  93. sbuf = "NOTICE " + nickName + " :Wrong password.\n";
  94. }
  95. handleMsgActions(message,action,nickName,ident,command,args);
  96. }
  97. void loadMessageData() {
  98. message = rbuf.substr(rbuf.find(":")+1);
  99. message = message.substr(0,message.size()-2); // \r\n raus machen
  100.  
  101. action = message.substr(message.find(" ")+1);
  102. action = action.substr(0,action.find(" "));
  103.  
  104. nickName = message.substr(0,message.find("!"));
  105. ident = message.substr(message.find("@")+1);
  106. ident = ident.substr(0,ident.find(" "));
  107. ident = nickName + "@" + ident;
  108. message = message.substr(message.find(":")+1);
  109. command = message.substr(0,message.find(" "));
  110. args = message.substr(message.find(" ")+1);
  111.  
  112. postChannel = rbuf.substr(rbuf.find(" ")+1);
  113. postChannel = postChannel.substr(postChannel.find(" ")+1);
  114. postChannel = postChannel.substr(0,postChannel.find(" "));
  115. toLower(command);
  116. toLower(args);
  117. omnom = false;
  118. if (nickName == "OmnomIRC" || nickName == "Netham45_") {
  119. nickName = message.substr(0,message.find(">"));
  120. nickName = nickName.substr(message.find("<")+1);
  121. message = message.substr(message.find("> ")+2);
  122. command = message.substr(0,message.find(" "));
  123. args = message.substr(message.find(" ")+1);
  124. omnom = true;
  125. }
  126. handleQuitMessage();
  127. if (postChannel==nick)
  128. handleMsgCommands();
  129. else
  130. handleNormalActions(message,action,nickName,ident,command,args,postChannel,omnom);
  131. if (sbuf!="")
  132. sendSbuf();
  133. sbuf = "";
  134. }
  135. void handlePing() {
  136. if (rbuf.find("PING")==0) { //ping regeln
  137. sbuf = rbuf.replace(0,4,"PONG");
  138. send(sock, sbuf.c_str(), sbuf.size(), 0);
  139. }
  140. }
  141. void mainLoop() {
  142. int brecv;
  143. int previousTime = time(NULL);
  144. while (true) {
  145. while((brecv=recv(sock,charTemp,sizeof(charTemp),0)) > 0) {
  146. rbuf.assign(charTemp, brecv);
  147. std::cout << rbuf << std::endl;
  148. handlePing();
  149. if (rbuf.substr(0,1)==":")
  150. loadMessageData();
  151. }
  152. if (previousTime+10 <= time(NULL))
  153. rejoin();
  154. }
  155. }
  156. };
  157.  
  158. #endif
  159.  
  160.  
  161.  
  162.  
  163. /****** filename: md5.h ******/
  164. #ifndef MD5_H
  165. #define MD5_H
  166. /* MD5
  167. converted to C++ class by Frank Thilo (thilo@unix-ag.org)
  168. for bzflag (http://www.bzflag.org)
  169.  
  170. based on:
  171.  
  172. md5.h and md5.c
  173. reference implemantion of RFC 1321
  174.  
  175. Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
  176. rights reserved.
  177.  
  178. License to copy and use this software is granted provided that it
  179. is identified as the "RSA Data Security, Inc. MD5 Message-Digest
  180. Algorithm" in all material mentioning or referencing this software
  181. or this function.
  182.  
  183. License is also granted to make and use derivative works provided
  184. that such works are identified as "derived from the RSA Data
  185. Security, Inc. MD5 Message-Digest Algorithm" in all material
  186. mentioning or referencing the derived work.
  187.  
  188. RSA Data Security, Inc. makes no representations concerning either
  189. the merchantability of this software or the suitability of this
  190. software for any particular purpose. It is provided "as is"
  191. without express or implied warranty of any kind.
  192.  
  193. These notices must be retained in any copies of any part of this
  194. documentation and/or software.
  195.  
  196. */
  197.  
  198. /* interface header */
  199.  
  200.  
  201. #include <string>
  202. #include <iostream>
  203.  
  204.  
  205. // a small class for calculating MD5 hashes of strings or byte arrays
  206. // it is not meant to be fast or secure
  207. //
  208. // usage: 1) feed it blocks of uchars with update()
  209. // 2) finalize()
  210. // 3) get hexdigest() string
  211. // or
  212. // MD5(std::string).hexdigest()
  213. //
  214. // assumes that char is 8 bit and int is 32 bit
  215. class MD5 {
  216. public:
  217. typedef unsigned int size_type; // must be 32bit
  218.  
  219. MD5();
  220. MD5(const std::string& text);
  221. void update(const unsigned char *buf, size_type length);
  222. void update(const char *buf, size_type length);
  223. MD5& finalize();
  224. std::string hexdigest() const;
  225. friend std::ostream& operator<<(std::ostream&, MD5 md5);
  226.  
  227. private:
  228. void init();
  229. typedef unsigned char uint1; // 8bit
  230. typedef unsigned int uint4; // 32bit
  231. enum {blocksize = 64}; // VC6 won't eat a const static int here
  232.  
  233. void transform(const uint1 block[blocksize]);
  234. static void decode(uint4 output[], const uint1 input[], size_type len);
  235. static void encode(uint1 output[], const uint4 input[], size_type len);
  236.  
  237. bool finalized;
  238. uint1 buffer[blocksize]; // bytes that didn't fit in last 64 byte chunk
  239. uint4 count[2]; // 64bit counter for number of bits (lo, hi)
  240. uint4 state[4]; // digest so far
  241. uint1 digest[16]; // the result
  242.  
  243. // low level logic operations
  244. static inline uint4 F(uint4 x, uint4 y, uint4 z);
  245. static inline uint4 G(uint4 x, uint4 y, uint4 z);
  246. static inline uint4 H(uint4 x, uint4 y, uint4 z);
  247. static inline uint4 I(uint4 x, uint4 y, uint4 z);
  248. static inline uint4 rotate_left(uint4 x, int n);
  249. static inline void FF(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac);
  250. static inline void GG(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac);
  251. static inline void HH(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac);
  252. static inline void II(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac);
  253. };
  254.  
  255. std::string md5(const std::string str);
  256.  
  257.  
  258. /* system implementation headers */
  259. #include <stdio.h>
  260.  
  261.  
  262. // Constants for MD5Transform routine.
  263. #define S11 7
  264. #define S12 12
  265. #define S13 17
  266. #define S14 22
  267. #define S21 5
  268. #define S22 9
  269. #define S23 14
  270. #define S24 20
  271. #define S31 4
  272. #define S32 11
  273. #define S33 16
  274. #define S34 23
  275. #define S41 6
  276. #define S42 10
  277. #define S43 15
  278. #define S44 21
  279.  
  280. ///////////////////////////////////////////////
  281.  
  282. // F, G, H and I are basic MD5 functions.
  283. inline MD5::uint4 MD5::F(uint4 x, uint4 y, uint4 z) {
  284. return x&y | ~x&z;
  285. }
  286.  
  287. inline MD5::uint4 MD5::G(uint4 x, uint4 y, uint4 z) {
  288. return x&z | y&~z;
  289. }
  290.  
  291. inline MD5::uint4 MD5::H(uint4 x, uint4 y, uint4 z) {
  292. return x^y^z;
  293. }
  294.  
  295. inline MD5::uint4 MD5::I(uint4 x, uint4 y, uint4 z) {
  296. return y ^ (x | ~z);
  297. }
  298.  
  299. // rotate_left rotates x left n bits.
  300. inline MD5::uint4 MD5::rotate_left(uint4 x, int n) {
  301. return (x << n) | (x >> (32-n));
  302. }
  303.  
  304. // FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
  305. // Rotation is separate from addition to prevent recomputation.
  306. inline void MD5::FF(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac) {
  307. a = rotate_left(a+ F(b,c,d) + x + ac, s) + b;
  308. }
  309.  
  310. inline void MD5::GG(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac) {
  311. a = rotate_left(a + G(b,c,d) + x + ac, s) + b;
  312. }
  313.  
  314. inline void MD5::HH(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac) {
  315. a = rotate_left(a + H(b,c,d) + x + ac, s) + b;
  316. }
  317.  
  318. inline void MD5::II(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac) {
  319. a = rotate_left(a + I(b,c,d) + x + ac, s) + b;
  320. }
  321.  
  322. //////////////////////////////////////////////
  323.  
  324. // default ctor, just initailize
  325. MD5::MD5() {
  326. init();
  327. }
  328.  
  329. //////////////////////////////////////////////
  330.  
  331. // nifty shortcut ctor, compute MD5 for string and finalize it right away
  332. MD5::MD5(const std::string &text) {
  333. init();
  334. update(text.c_str(), text.length());
  335. finalize();
  336. }
  337.  
  338. //////////////////////////////
  339.  
  340. void MD5::init() {
  341. finalized=false;
  342.  
  343. count[0] = 0;
  344. count[1] = 0;
  345.  
  346. // load magic initialization constants.
  347. state[0] = 0x67452301;
  348. state[1] = 0xefcdab89;
  349. state[2] = 0x98badcfe;
  350. state[3] = 0x10325476;
  351. }
  352.  
  353. //////////////////////////////
  354.  
  355. // decodes input (unsigned char) into output (uint4). Assumes len is a multiple of 4.
  356. void MD5::decode(uint4 output[], const uint1 input[], size_type len) {
  357. for (unsigned int i = 0, j = 0; j < len; i++, j += 4)
  358. output[i] = ((uint4)input[j]) | (((uint4)input[j+1]) << 8) |
  359. (((uint4)input[j+2]) << 16) | (((uint4)input[j+3]) << 24);
  360. }
  361.  
  362. //////////////////////////////
  363.  
  364. // encodes input (uint4) into output (unsigned char). Assumes len is
  365. // a multiple of 4.
  366. void MD5::encode(uint1 output[], const uint4 input[], size_type len) {
  367. for (size_type i = 0, j = 0; j < len; i++, j += 4) {
  368. output[j] = input[i] & 0xff;
  369. output[j+1] = (input[i] >> 8) & 0xff;
  370. output[j+2] = (input[i] >> 16) & 0xff;
  371. output[j+3] = (input[i] >> 24) & 0xff;
  372. }
  373. }
  374.  
  375. //////////////////////////////
  376.  
  377. // apply MD5 algo on a block
  378. void MD5::transform(const uint1 block[blocksize]) {
  379. uint4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
  380. decode (x, block, blocksize);
  381.  
  382. /* Round 1 */
  383. FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
  384. FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
  385. FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
  386. FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
  387. FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
  388. FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
  389. FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
  390. FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
  391. FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
  392. FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
  393. FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
  394. FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
  395. FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
  396. FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
  397. FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
  398. FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
  399.  
  400. /* Round 2 */
  401. GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
  402. GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
  403. GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
  404. GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
  405. GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
  406. GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */
  407. GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
  408. GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
  409. GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
  410. GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
  411. GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */
  412. GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
  413. GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
  414. GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
  415. GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
  416. GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
  417.  
  418. /* Round 3 */
  419. HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
  420. HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
  421. HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
  422. HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
  423. HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
  424. HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
  425. HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
  426. HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
  427. HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
  428. HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
  429. HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
  430. HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */
  431. HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
  432. HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
  433. HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
  434. HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */
  435.  
  436. /* Round 4 */
  437. II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */
  438. II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */
  439. II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
  440. II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */
  441. II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
  442. II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */
  443. II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
  444. II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */
  445. II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */
  446. II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
  447. II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */
  448. II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
  449. II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */
  450. II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
  451. II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */
  452. II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */
  453.  
  454. state[0] += a;
  455. state[1] += b;
  456. state[2] += c;
  457. state[3] += d;
  458.  
  459. // Zeroize sensitive information.
  460. memset(x, 0, sizeof x);
  461. }
  462.  
  463. //////////////////////////////
  464.  
  465. // MD5 block update operation. Continues an MD5 message-digest
  466. // operation, processing another message block
  467. void MD5::update(const unsigned char input[], size_type length) {
  468. // compute number of bytes mod 64
  469. size_type index = count[0] / 8 % blocksize;
  470.  
  471. // Update number of bits
  472. if ((count[0] += (length << 3)) < (length << 3))
  473. count[1]++;
  474. count[1] += (length >> 29);
  475.  
  476. // number of bytes we need to fill in buffer
  477. size_type firstpart = 64 - index;
  478.  
  479. size_type i;
  480.  
  481. // transform as many times as possible.
  482. if (length >= firstpart) {
  483. // fill buffer first, transform
  484. memcpy(&buffer[index], input, firstpart);
  485. transform(buffer);
  486.  
  487. // transform chunks of blocksize (64 bytes)
  488. for (i = firstpart; i + blocksize <= length; i += blocksize)
  489. transform(&input[i]);
  490.  
  491. index = 0;
  492. }
  493. else
  494. i = 0;
  495.  
  496. // buffer remaining input
  497. memcpy(&buffer[index], &input[i], length-i);
  498. }
  499.  
  500. //////////////////////////////
  501.  
  502. // for convenience provide a verson with signed char
  503. void MD5::update(const char input[], size_type length) {
  504. update((const unsigned char*)input, length);
  505. }
  506.  
  507. //////////////////////////////
  508.  
  509. // MD5 finalization. Ends an MD5 message-digest operation, writing the
  510. // the message digest and zeroizing the context.
  511. MD5& MD5::finalize() {
  512. static unsigned char padding[64] = {
  513. 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  514. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  515. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
  516. };
  517.  
  518. if (!finalized) {
  519. // Save number of bits
  520. unsigned char bits[8];
  521. encode(bits, count, 8);
  522.  
  523. // pad out to 56 mod 64.
  524. size_type index = count[0] / 8 % 64;
  525. size_type padLen = (index < 56) ? (56 - index) : (120 - index);
  526. update(padding, padLen);
  527.  
  528. // Append length (before padding)
  529. update(bits, 8);
  530.  
  531. // Store state in digest
  532. encode(digest, state, 16);
  533.  
  534. // Zeroize sensitive information.
  535. memset(buffer, 0, sizeof buffer);
  536. memset(count, 0, sizeof count);
  537.  
  538. finalized=true;
  539. }
  540.  
  541. return *this;
  542. }
  543.  
  544. //////////////////////////////
  545.  
  546. // return hex representation of digest as string
  547. std::string MD5::hexdigest() const {
  548. if (!finalized)
  549. return "";
  550.  
  551. char buf[33];
  552. for (int i=0; i<16; i++)
  553. sprintf(buf+i*2, "%02x", digest[i]);
  554. buf[32]=0;
  555.  
  556. return std::string(buf);
  557. }
  558.  
  559. //////////////////////////////
  560.  
  561. std::ostream& operator<<(std::ostream& out, MD5 md5) {
  562. return out << md5.hexdigest();
  563. }
  564.  
  565. //////////////////////////////
  566.  
  567. std::string md5(const std::string str) {
  568. MD5 md5 = MD5(str);
  569.  
  570. return md5.hexdigest();
  571. }
  572. #endif
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement