PageRenderTime 60ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/src/socket.c

https://github.com/telestrial/mud
C | 1431 lines | 913 code | 236 blank | 282 comment | 234 complexity | 8005079de39b4889cd8dafffd06dd61f MD5 | raw file
  1. //*****************************************************************************
  2. //
  3. // socket.c
  4. //
  5. // This file contains the socket code, used for accepting new connections as
  6. // well as reading and writing to sockets, and closing down unused sockets.
  7. //
  8. //*****************************************************************************
  9. #include "wrapsock.h"
  10. #include <netdb.h>
  11. #include <sys/ioctl.h>
  12. #include <arpa/inet.h>
  13. #include <zlib.h>
  14. #include <pthread.h>
  15. #include "mud.h"
  16. #include "character.h"
  17. #include "account.h"
  18. #include "save.h"
  19. #include "utils.h"
  20. #include "socket.h"
  21. #include "auxiliary.h"
  22. #include "hooks.h"
  23. #include "scripts/scripts.h"
  24. #include "scripts/pyplugs.h"
  25. #include "dyn_vars/dyn_vars.h"
  26. //*****************************************************************************
  27. // optional modules
  28. //*****************************************************************************
  29. #ifdef MODULE_ALIAS
  30. #include "alias/alias.h"
  31. #endif
  32. // provides a unique identifier number to every socket that connects to the
  33. // mud. Used mostly for referring to sockets in Python
  34. #define START_SOCK_UID 1
  35. int next_sock_uid = START_SOCK_UID;
  36. //
  37. // Here it is... the big ol' datastructure for sockets. Yum.
  38. struct socket_data {
  39. CHAR_DATA * player;
  40. ACCOUNT_DATA * account;
  41. char * hostname;
  42. char inbuf[MAX_INPUT_LEN];
  43. BUFFER * next_command;
  44. BUFFER * iac_sequence;
  45. // char next_command[MAX_BUFFER];
  46. bool cmd_read;
  47. bool bust_prompt;
  48. bool closed;
  49. int lookup_status;
  50. int control;
  51. int uid;
  52. double idle; // how many pulses have we been idle for?
  53. char * page_string; // the string that has been paged to us
  54. int curr_page; // the current page we're on
  55. int tot_pages; // the total number of pages the string has
  56. BUFFER * text_editor; // where we do our actual work
  57. BUFFER * outbuf; // our buffer of pending output
  58. LIST * input_handlers;// a stack of our input handlers and prompts
  59. LIST * input; // lines of input we have received
  60. LIST * command_hist; // the commands we've executed in the past
  61. unsigned char compressing; /* MCCP support */
  62. z_stream * out_compress; /* MCCP support */
  63. unsigned char * out_compress_buf; /* MCCP support */
  64. AUX_TABLE * auxiliary; // auxiliary data installed by other modules
  65. };
  66. //
  67. // contains an input handler and the socket prompt in one structure, so they
  68. // can be stored together in the socket_data. Allows for the option of Python
  69. // or C input handlers and prompt pairs.
  70. typedef struct input_handler_data {
  71. void *handler; // (* handler)(SOCKET_DATA *, char *);
  72. void *prompt; // (* prompt)(SOCKET_DATA *);
  73. bool python;
  74. char *state; // what state does this input handler represent?
  75. } IH_PAIR;
  76. //
  77. // required for looking up a socket's IP in a new thread
  78. typedef struct lookup_data {
  79. SOCKET_DATA * dsock; // the socket we wish to do a hostlookup on
  80. char * buf; // the buffer it should be stored in
  81. } LOOKUP_DATA;
  82. /* global variables */
  83. fd_set fSet; /* the socket list for polling */
  84. fd_set rFd;
  85. /* mccp support */
  86. const unsigned char compress_will [] = { IAC, WILL, TELOPT_COMPRESS, '\0' };
  87. const unsigned char compress_will2 [] = { IAC, WILL, TELOPT_COMPRESS2, '\0' };
  88. // used to delete an input handler pair
  89. void deleteInputHandler(IH_PAIR *pair) {
  90. if(pair->python) {
  91. Py_XDECREF((PyObject *)pair->handler);
  92. Py_XDECREF((PyObject *)pair->prompt);
  93. }
  94. if(pair->state) free(pair->state);
  95. free(pair);
  96. }
  97. /*
  98. * Init_socket()
  99. *
  100. * Used at bootup to get a free
  101. * socket to run the server from.
  102. */
  103. int init_socket()
  104. {
  105. struct sockaddr_in my_addr;
  106. int sockfd, reuse = 1;
  107. /* let's grab a socket */
  108. sockfd = socket(AF_INET, SOCK_STREAM, 0);
  109. /* setting the correct values */
  110. my_addr.sin_family = AF_INET;
  111. my_addr.sin_addr.s_addr = INADDR_ANY;
  112. my_addr.sin_port = htons(mudport);
  113. /* this actually fixes any problems with threads */
  114. if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int)) == -1)
  115. {
  116. perror("Error in setsockopt()");
  117. exit(1);
  118. }
  119. /* bind the port */
  120. bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr));
  121. /* start listening already :) */
  122. listen(sockfd, 3);
  123. /* return the socket */
  124. return sockfd;
  125. }
  126. /*
  127. * New_socket()
  128. *
  129. * Initializes a new socket, get's the hostname
  130. * and puts it in the active socket_list.
  131. */
  132. SOCKET_DATA *new_socket(int sock)
  133. {
  134. struct sockaddr_in sock_addr;
  135. pthread_attr_t attr;
  136. pthread_t thread_lookup;
  137. LOOKUP_DATA * lData;
  138. SOCKET_DATA * sock_new;
  139. int argp = 1;
  140. socklen_t size;
  141. /* initialize threads */
  142. pthread_attr_init(&attr);
  143. pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
  144. /* create and clear the socket */
  145. sock_new = calloc(1, sizeof(SOCKET_DATA));
  146. /* attach the new connection to the socket list */
  147. FD_SET(sock, &fSet);
  148. /* clear out the socket */
  149. clear_socket(sock_new, sock);
  150. sock_new->closed = FALSE;
  151. /* set the socket as non-blocking */
  152. ioctl(sock, FIONBIO, &argp);
  153. /* update the socket list and table */
  154. listPut(socket_list, sock_new);
  155. propertyTablePut(sock_table, sock_new);
  156. /* do a host lookup */
  157. size = sizeof(sock_addr);
  158. if (getpeername(sock, (struct sockaddr *) &sock_addr, &size) < 0)
  159. {
  160. perror("New_socket: getpeername");
  161. sock_new->hostname = strdup("unknown");
  162. }
  163. else
  164. {
  165. /* set the IP number as the temporary hostname */
  166. sock_new->hostname = strdup(inet_ntoa(sock_addr.sin_addr));
  167. if (!compares(sock_new->hostname, "127.0.0.1"))
  168. {
  169. /* allocate some memory for the lookup data */
  170. if ((lData = malloc(sizeof(*lData))) == NULL)
  171. {
  172. bug("New_socket: Cannot allocate memory for lookup data.");
  173. abort();
  174. }
  175. /* Set the lookup_data for use in lookup_address() */
  176. lData->buf = strdup((char *) &sock_addr.sin_addr);
  177. lData->dsock = sock_new;
  178. /* dispatch the lookup thread */
  179. pthread_create(&thread_lookup, &attr, &lookup_address, (void*) lData);
  180. }
  181. else sock_new->lookup_status++;
  182. }
  183. /* negotiate compression */
  184. // text_to_buffer(sock_new, (char *) compress_will2);
  185. // text_to_buffer(sock_new, (char *) compress_will);
  186. /* send the greeting */
  187. // text_to_buffer(sock_new, bufferString(greeting));
  188. /* everything went as it was supposed to */
  189. return sock_new;
  190. }
  191. /*
  192. * Close_socket()
  193. *
  194. * Will close one socket directly, freeing all
  195. * resources and making the socket availably on
  196. * the socket free_list.
  197. */
  198. void close_socket(SOCKET_DATA *dsock, bool reconnect)
  199. {
  200. if (dsock->lookup_status > TSTATE_DONE) return;
  201. dsock->lookup_status += 2;
  202. /* remove the socket from the polling list */
  203. FD_CLR(dsock->control, &fSet);
  204. /* remove ourself from the list */
  205. //
  206. // NO! We don't want to remove ourself from the list just yet.
  207. // We will do that the next time we show up in the game_loop.
  208. // removing now will not close the socket completely. Why, I'm not
  209. // entirely sure... but it doesn't ;)
  210. // - Geoff, Dec26/04
  211. //
  212. // listRemove(socket_list, dsock);
  213. //
  214. // we have a character, and it's one that's
  215. // not in the process of being created
  216. if (dsock->player && charGetUID(dsock->player) != NOBODY) {
  217. if (reconnect)
  218. text_to_socket(dsock, "This connection has been taken over.\r\n");
  219. else {
  220. charSetSocket(dsock->player, NULL);
  221. log_string("Closing link to %s", charGetName(dsock->player));
  222. }
  223. }
  224. else if(dsock->player) {
  225. charSetSocket(dsock->player, NULL);
  226. extract_mobile(dsock->player);
  227. }
  228. if(dsock->account) {
  229. if(account_exists(accountGetName(dsock->account)))
  230. unreference_account(dsock->account);
  231. else
  232. deleteAccount(dsock->account);
  233. }
  234. /* set the closed state */
  235. dsock->closed = TRUE;
  236. // dsock->state = STATE_CLOSED;
  237. }
  238. /*
  239. * Read_from_socket()
  240. *
  241. * Reads one line from the socket, storing it
  242. * in a buffer for later use. Will also close
  243. * the socket if it tries a buffer overflow.
  244. */
  245. bool read_from_socket(SOCKET_DATA *dsock)
  246. {
  247. int size;
  248. extern int errno;
  249. /* check for buffer overflows, and drop connection in that case */
  250. size = strlen(dsock->inbuf);
  251. if (size >= sizeof(dsock->inbuf) - 2)
  252. {
  253. text_to_socket(dsock, "\n\r!!!! Input Overflow !!!!\n\r");
  254. return FALSE;
  255. }
  256. /* start reading from the socket */
  257. for (;;)
  258. {
  259. int sInput;
  260. int wanted = sizeof(dsock->inbuf) - 2 - size;
  261. sInput = read(dsock->control, dsock->inbuf + size, wanted);
  262. if (sInput > 0)
  263. {
  264. size += sInput;
  265. if (dsock->inbuf[size-1] == '\n' || dsock->inbuf[size-1] == '\r')
  266. break;
  267. }
  268. else if (sInput == 0)
  269. {
  270. log_string("Read_from_socket: EOF");
  271. return FALSE;
  272. }
  273. else if (errno == EAGAIN || sInput == wanted)
  274. break;
  275. else
  276. {
  277. perror("Read_from_socket");
  278. return FALSE;
  279. }
  280. }
  281. dsock->inbuf[size] = '\0';
  282. return TRUE;
  283. }
  284. /*
  285. * Text_to_socket()
  286. *
  287. * Sends text directly to the socket,
  288. * will compress the data if needed.
  289. */
  290. bool text_to_socket(SOCKET_DATA *dsock, const char *txt)
  291. {
  292. int iBlck, iPtr, iWrt = 0, length, control = dsock->control;
  293. length = strlen(txt);
  294. /* write compressed */
  295. if (dsock && dsock->out_compress)
  296. {
  297. dsock->out_compress->next_in = (unsigned char *) txt;
  298. dsock->out_compress->avail_in = length;
  299. while (dsock->out_compress->avail_in)
  300. {
  301. dsock->out_compress->avail_out = COMPRESS_BUF_SIZE - (dsock->out_compress->next_out - dsock->out_compress_buf);
  302. if (dsock->out_compress->avail_out)
  303. {
  304. int status = deflate(dsock->out_compress, Z_SYNC_FLUSH);
  305. if (status != Z_OK)
  306. return FALSE;
  307. }
  308. length = dsock->out_compress->next_out - dsock->out_compress_buf;
  309. if (length > 0)
  310. {
  311. for (iPtr = 0; iPtr < length; iPtr += iWrt)
  312. {
  313. iBlck = UMIN(length - iPtr, 4096);
  314. if ((iWrt = write(control, dsock->out_compress_buf + iPtr, iBlck)) < 0)
  315. {
  316. perror("Text_to_socket (compressed):");
  317. return FALSE;
  318. }
  319. }
  320. if (iWrt <= 0) break;
  321. if (iPtr > 0)
  322. {
  323. if (iPtr < length)
  324. memmove(dsock->out_compress_buf, dsock->out_compress_buf + iPtr, length - iPtr);
  325. dsock->out_compress->next_out = dsock->out_compress_buf + length - iPtr;
  326. }
  327. }
  328. }
  329. return TRUE;
  330. }
  331. /* write uncompressed */
  332. for (iPtr = 0; iPtr < length; iPtr += iWrt)
  333. {
  334. iBlck = UMIN(length - iPtr, 4096);
  335. if ((iWrt = write(control, txt + iPtr, iBlck)) < 0)
  336. {
  337. perror("Text_to_socket:");
  338. return FALSE;
  339. }
  340. }
  341. return TRUE;
  342. }
  343. void send_to_socket( SOCKET_DATA *dsock, const char *format, ...) {
  344. if(format && *format) {
  345. static char buf[MAX_BUFFER];
  346. va_list args;
  347. va_start(args, format);
  348. vsnprintf(buf, MAX_BUFFER, format, args);
  349. va_end(args);
  350. text_to_buffer(dsock, buf);
  351. }
  352. }
  353. /*
  354. * Text_to_buffer()
  355. *
  356. * Stores outbound text in a buffer, where it will
  357. * stay untill it is flushed in the gameloop.
  358. *
  359. * Will also parse ANSI colors and other tags.
  360. */
  361. void text_to_buffer(SOCKET_DATA *dsock, const char *txt)
  362. {
  363. // if we're at the head of the outbuf and haven't entered a command,
  364. // also copy a newline so we're not printing in front of the prompt
  365. if(bufferLength(dsock->outbuf) == 0 && !dsock->bust_prompt)
  366. bufferCat(dsock->outbuf, "\r\n");
  367. /* add data to buffer */
  368. bufferCat(dsock->outbuf, txt);
  369. }
  370. //
  371. // read an IAC sequence from the socket's input buffer. When we hit a
  372. // termination point, send a hook and clear he buffer. Return how many
  373. // characters to skip ahead in the input handler
  374. int read_iac_sequence(SOCKET_DATA *dsock, int start) {
  375. // are we doing subnegotiation?
  376. bool subneg = strchr(bufferString(dsock->iac_sequence), SB) != NULL;
  377. int i = start;
  378. bool done = FALSE;
  379. for(; dsock->inbuf[i] != '\0'; i++) {
  380. // are we looking for IAC?
  381. if(bufferLength(dsock->iac_sequence) == 0) {
  382. // Oops! Something is broken
  383. if(dsock->inbuf[i] != (signed char) IAC)
  384. return 0;
  385. else
  386. bufferCatCh(dsock->iac_sequence, IAC);
  387. }
  388. // are we looking for a command?
  389. else if(bufferLength(dsock->iac_sequence) == 1) {
  390. // did we find subnegotiation?
  391. if(dsock->inbuf[i] == (signed char) SB) {
  392. bufferCatCh(dsock->iac_sequence, dsock->inbuf[i]);
  393. subneg = TRUE;
  394. }
  395. // basic three-character command
  396. else if(dsock->inbuf[i] == (signed char) WILL ||
  397. dsock->inbuf[i] == (signed char) WONT ||
  398. dsock->inbuf[i] == (signed char) DO ||
  399. dsock->inbuf[i] == (signed char) DONT)
  400. bufferCatCh(dsock->iac_sequence, dsock->inbuf[i]);
  401. // something went wrong
  402. else {
  403. bufferClear(dsock->iac_sequence);
  404. return 2;
  405. }
  406. }
  407. // are we doing subnegotiation?
  408. else if(subneg) {
  409. int last_i = bufferLength(dsock->iac_sequence) - 2;
  410. signed char last = bufferString(dsock->iac_sequence)[last_i];
  411. bufferCatCh(dsock->iac_sequence, dsock->inbuf[i]);
  412. // have hit the end of the subnegotiation? Break out if so
  413. if(last == (signed char) IAC && dsock->inbuf[i] == (signed char) SE) {
  414. done = TRUE;
  415. break;
  416. }
  417. }
  418. // this is the end
  419. else {
  420. bufferCatCh(dsock->iac_sequence, dsock->inbuf[i]);
  421. done = TRUE;
  422. break;
  423. }
  424. }
  425. int len = i - start + (dsock->inbuf[i] == '\0' ? 0 : 1);
  426. // broadcast the message we parsed, and prepare for the next sequence
  427. if(done == TRUE) {
  428. hookRun("receive_iac",
  429. hookBuildInfo("sk str", dsock,bufferString(dsock->iac_sequence)));
  430. bufferClear(dsock->iac_sequence);
  431. }
  432. return len;
  433. }
  434. void next_cmd_from_buffer(SOCKET_DATA *dsock) {
  435. // do we have stuff in our input list? If so, use that instead of inbuf
  436. dsock->cmd_read = FALSE;
  437. if(listSize(dsock->input) > 0) {
  438. char *cmd = listPop(dsock->input);
  439. bufferClear(dsock->next_command);
  440. bufferCat(dsock->next_command, cmd);
  441. dsock->cmd_read = TRUE;
  442. dsock->bust_prompt = TRUE;
  443. free(cmd);
  444. }
  445. else {
  446. int i = 0, cmd_end = -1;
  447. // are we building an IAC command? Try to continue it
  448. if(bufferLength(dsock->iac_sequence) > 0)
  449. i += read_iac_sequence(dsock, 0);
  450. // copy over characters until we hit a newline, an IAC command, or \0
  451. for(; dsock->inbuf[i] != '\0' && cmd_end < 0; i++) {
  452. switch(dsock->inbuf[i]) {
  453. default:
  454. // append us to the command
  455. bufferCatCh(dsock->next_command, dsock->inbuf[i]);
  456. break;
  457. case '\n':
  458. // command end found
  459. cmd_end = ++i;
  460. case '\r':
  461. // ignore \r ... only pay attention to \n
  462. break;
  463. case (signed char) IAC:
  464. i += read_iac_sequence(dsock, i) - 1;
  465. break;
  466. }
  467. if(cmd_end >= 0)
  468. break;
  469. }
  470. // move the context of inbuf down
  471. int begin = 0;
  472. while(dsock->inbuf[i] != '\0')
  473. dsock->inbuf[begin++] = dsock->inbuf[i++];
  474. dsock->inbuf[begin] = '\0';
  475. // did we find a command?
  476. if(cmd_end >= 0) {
  477. dsock->cmd_read = TRUE;
  478. dsock->bust_prompt = TRUE;
  479. }
  480. }
  481. }
  482. bool flush_output(SOCKET_DATA *dsock) {
  483. bool success = TRUE;
  484. BUFFER *buf = NULL;
  485. // run any hooks prior to flushing our text
  486. hookRun("flush", hookBuildInfo("sk", dsock));
  487. // quit if we have no output and don't need/can't have a prompt
  488. if(bufferLength(dsock->outbuf) <= 0 &&
  489. (!dsock->bust_prompt || !socketHasPrompt(dsock)))
  490. return success;
  491. buf = newBuffer(1);
  492. // send our outbound text
  493. if(bufferLength(dsock->outbuf) > 0) {
  494. hookRun("process_outbound_text", hookBuildInfo("sk", dsock));
  495. hookRun("finalize_outbound_text", hookBuildInfo("sk", dsock));
  496. //success = text_to_socket(dsock, bufferString(dsock->outbuf));
  497. bufferCat(buf, bufferString(dsock->outbuf));
  498. bufferClear(dsock->outbuf);
  499. }
  500. // send our prompt
  501. if(dsock->bust_prompt && success) {
  502. socketShowPrompt(dsock);
  503. hookRun("process_outbound_prompt", hookBuildInfo("sk", dsock));
  504. hookRun("finalize_outbound_prompt", hookBuildInfo("sk", dsock));
  505. //success = text_to_socket(dsock, bufferString(dsock->outbuf));
  506. bufferCat(buf, bufferString(dsock->outbuf));
  507. bufferClear(dsock->outbuf);
  508. dsock->bust_prompt = FALSE;
  509. }
  510. success = text_to_socket(dsock, bufferString(buf));
  511. deleteBuffer(buf);
  512. // return our success
  513. return success;
  514. }
  515. //*****************************************************************************
  516. //
  517. // SOCKET MAINTENANCE
  518. //
  519. // Functions below this point are mainly concerned with the upkeep and
  520. // maintenance of sockets (making sure they are initialized, garbage collected,
  521. // getting their addresses, etc...)
  522. //
  523. //*****************************************************************************
  524. void deleteSocket(SOCKET_DATA *sock) {
  525. if(sock->hostname) free(sock->hostname);
  526. if(sock->page_string) free(sock->page_string);
  527. if(sock->text_editor) deleteBuffer(sock->text_editor);
  528. if(sock->outbuf) deleteBuffer(sock->outbuf);
  529. if(sock->next_command) deleteBuffer(sock->next_command);
  530. if(sock->iac_sequence) deleteBuffer(sock->iac_sequence);
  531. if(sock->input_handlers)deleteListWith(sock->input_handlers,deleteInputHandler);
  532. if(sock->input) deleteListWith(sock->input, free);
  533. if(sock->command_hist) deleteListWith(sock->command_hist, free);
  534. if(sock->auxiliary) deleteAuxiliaryData(sock->auxiliary);
  535. free(sock);
  536. }
  537. void clear_socket(SOCKET_DATA *sock_new, int sock)
  538. {
  539. if(sock_new->page_string) free(sock_new->page_string);
  540. if(sock_new->text_editor) deleteBuffer(sock_new->text_editor);
  541. if(sock_new->outbuf) deleteBuffer(sock_new->outbuf);
  542. if(sock_new->next_command) deleteBuffer(sock_new->next_command);
  543. if(sock_new->iac_sequence) deleteBuffer(sock_new->iac_sequence);
  544. if(sock_new->input_handlers) deleteListWith(sock_new->input_handlers, deleteInputHandler);
  545. if(sock_new->auxiliary) deleteAuxiliaryData(sock_new->auxiliary);
  546. if(sock_new->input) deleteListWith(sock_new->input, free);
  547. if(sock_new->command_hist) deleteListWith(sock_new->command_hist, free);
  548. bzero(sock_new, sizeof(*sock_new));
  549. sock_new->auxiliary = newAuxiliaryData(AUXILIARY_TYPE_SOCKET);
  550. sock_new->input_handlers = newList();
  551. sock_new->input = newList();
  552. sock_new->command_hist = newList();
  553. sock_new->control = sock;
  554. sock_new->lookup_status = TSTATE_LOOKUP;
  555. sock_new->uid = next_sock_uid++;
  556. sock_new->text_editor = newBuffer(1);
  557. sock_new->outbuf = newBuffer(MAX_OUTPUT);
  558. sock_new->next_command = newBuffer(1);
  559. sock_new->iac_sequence = newBuffer(1);
  560. }
  561. /* does the lookup, changes the hostname, and dies */
  562. void *lookup_address(void *arg)
  563. {
  564. LOOKUP_DATA *lData = (LOOKUP_DATA *) arg;
  565. struct hostent *from = 0;
  566. // struct hostent ent;
  567. // char buf[16384];
  568. // int err;
  569. /* do the lookup and store the result at &from */
  570. from = gethostbyaddr(lData->buf, sizeof(lData->buf), AF_INET);
  571. /* did we get anything ? */
  572. if (from && from->h_name)
  573. {
  574. free(lData->dsock->hostname);
  575. lData->dsock->hostname = strdup(from->h_name);
  576. }
  577. /* set it ready to be closed or used */
  578. lData->dsock->lookup_status++;
  579. /* free the lookup data */
  580. free(lData->buf);
  581. free(lData);
  582. /* and kill the thread */
  583. pthread_exit(0);
  584. return NULL;
  585. }
  586. void recycle_sockets()
  587. {
  588. SOCKET_DATA *dsock;
  589. LIST_ITERATOR *sock_i = newListIterator(socket_list);
  590. ITERATE_LIST(dsock, sock_i) {
  591. if (dsock->lookup_status != TSTATE_CLOSED)
  592. continue;
  593. /* remove the socket from the main list */
  594. listRemove(socket_list, dsock);
  595. propertyTableRemove(sock_table, dsock->uid);
  596. /* close the socket */
  597. close(dsock->control);
  598. /* stop compression */
  599. compressEnd(dsock, dsock->compressing, TRUE);
  600. /* delete the socket from memory */
  601. deleteSocket(dsock);
  602. } deleteListIterator(sock_i);
  603. }
  604. /* reset all of the sockets' control values */
  605. void reconnect_copyover_sockets() {
  606. LIST_ITERATOR *sock_i = newListIterator(socket_list);
  607. SOCKET_DATA *sock = NULL;
  608. ITERATE_LIST(sock, sock_i)
  609. FD_SET(sock->control, &fSet);
  610. deleteListIterator(sock_i);
  611. }
  612. /* Recover from a copyover - load players */
  613. void copyover_recover() {
  614. CHAR_DATA *dMob;
  615. ACCOUNT_DATA *account;
  616. SOCKET_DATA *dsock;
  617. FILE *fp;
  618. char acct[100];
  619. char name[100];
  620. char host[MAX_BUFFER];
  621. int desc;
  622. log_string("Copyover recovery initiated");
  623. if ((fp = fopen(COPYOVER_FILE, "r")) == NULL) {
  624. log_string("Copyover file not found. Exitting.");
  625. exit (1);
  626. }
  627. /* In case something crashes - doesn't prevent reading */
  628. unlink(COPYOVER_FILE);
  629. for (;;) {
  630. fscanf(fp, "%d %s %s %s\n", &desc, acct, name, host);
  631. if (desc == -1)
  632. break;
  633. // Many thanks to Rhaelar for the help in finding this bug; clear_socket
  634. // does not like receiving freshly malloc'd data. We have to make sure
  635. // everything is zeroed before we pass it to clear_socket
  636. // dsock = malloc(sizeof(*dsock));
  637. dsock = calloc(1, sizeof(*dsock));
  638. clear_socket(dsock, desc);
  639. dsock->hostname = strdup(host);
  640. listPut(socket_list, dsock);
  641. propertyTablePut(sock_table, dsock);
  642. // load account data
  643. if((account = get_account(acct)) != NULL)
  644. socketSetAccount(dsock, account);
  645. // no luck!
  646. else {
  647. close_socket(dsock, FALSE);
  648. continue;
  649. }
  650. // load player data
  651. if ((dMob = get_player(name)) != NULL) {
  652. // attach to socket
  653. charSetSocket(dMob, dsock);
  654. socketSetChar(dsock, dMob);
  655. // try putting the character into the game
  656. // close the socket if we fail.
  657. if(!try_enter_game(dMob)) {
  658. // do not bother extracting, since we haven't entered the game yet
  659. unreference_player(socketGetChar(dsock));
  660. socketSetChar(dsock, NULL);
  661. close_socket(dsock, FALSE);
  662. continue;
  663. }
  664. }
  665. // no luck
  666. else {
  667. close_socket(dsock, FALSE);
  668. continue;
  669. }
  670. // Write something, and check if it goes error-free
  671. if (!text_to_socket(dsock, "\n\r <*> And before you know it, everything has changed <*>\n\r")) {
  672. close_socket(dsock, FALSE);
  673. continue;
  674. }
  675. // make sure the socket can be used
  676. dsock->bust_prompt = TRUE;
  677. dsock->lookup_status = TSTATE_DONE;
  678. // let our modules know we've finished copying over a socket
  679. hookRun("copyover_complete", hookBuildInfo("sk", dsock));
  680. // negotiate compression
  681. text_to_buffer(dsock, (char *) compress_will2);
  682. text_to_buffer(dsock, (char *) compress_will);
  683. }
  684. fclose(fp);
  685. // now, set all of the sockets' control to the new fSet
  686. reconnect_copyover_sockets();
  687. }
  688. void output_handler() {
  689. LIST_ITERATOR *sock_i = newListIterator(socket_list);
  690. SOCKET_DATA *sock = NULL;
  691. ITERATE_LIST(sock, sock_i) {
  692. /* if the player quits or get's disconnected */
  693. if(sock->closed)
  694. continue;
  695. /* Send all new data to the socket and close it if any errors occour */
  696. if (!flush_output(sock))
  697. close_socket(sock, FALSE);
  698. } deleteListIterator(sock_i);
  699. }
  700. void input_handler() {
  701. LIST_ITERATOR *sock_i = newListIterator(socket_list);
  702. SOCKET_DATA *sock = NULL;
  703. ITERATE_LIST(sock, sock_i) {
  704. // Close sockects we are unable to read from, or if we have no handler
  705. // to take in input
  706. if ((FD_ISSET(sock->control, &rFd) && !read_from_socket(sock)) ||
  707. listSize(sock->input_handlers) == 0) {
  708. close_socket(sock, FALSE);
  709. continue;
  710. }
  711. /* Ok, check for a new command */
  712. next_cmd_from_buffer(sock);
  713. // are we idling?
  714. if(!sock->cmd_read)
  715. sock->idle += 1.0 / PULSES_PER_SECOND;
  716. /* Is there a new command pending ? */
  717. else if (sock->cmd_read) {
  718. sock->idle = 0.0;
  719. IH_PAIR *pair = listGet(sock->input_handlers, 0);
  720. if(pair->python == FALSE) {
  721. void (* handler)(SOCKET_DATA *, char *) = pair->handler;
  722. char *cmddup = strdup(bufferString(sock->next_command));
  723. handler(sock, cmddup);
  724. free(cmddup);
  725. }
  726. else {
  727. PyObject *arglist = Py_BuildValue("Os", socketGetPyFormBorrowed(sock),
  728. bufferString(sock->next_command));
  729. PyObject *retval = PyEval_CallObject(pair->handler, arglist);
  730. // check for an error:
  731. if(retval == NULL)
  732. log_pyerr("Error with a Python input handler");
  733. // garbage collection
  734. Py_XDECREF(retval);
  735. Py_XDECREF(arglist);
  736. }
  737. // append our last command to the command history. History buffer is
  738. // 100 commands, so pop off the earliest command if we're going over
  739. listPut(sock->command_hist, strdup(bufferString(sock->next_command)));
  740. if(listSize(sock->command_hist) > 100)
  741. free(listRemoveNum(sock->command_hist, 100));
  742. bufferClear(sock->next_command);
  743. // we save whether or not we read a command until our next call to
  744. // input_handler(), at which time it is reset to FALSE if we didn't read
  745. // sock->cmd_read = FALSE;
  746. }
  747. #ifdef MODULE_ALIAS
  748. // ACK!! this is so yucky, but I can't think of a better way to do it...
  749. // if this command was put in place by an alias, decrement the alias_queue
  750. // counter by one. This counter is in place mainly so aliases do not end
  751. // up calling eachother and making us get stuck in an infinite loop.
  752. if(sock->player) {
  753. int alias_queue = charGetAliasesQueued(sock->player);
  754. if(alias_queue > 0)
  755. charSetAliasesQueued(sock->player, --alias_queue);
  756. }
  757. #endif
  758. } deleteListIterator(sock_i);
  759. }
  760. int count_pages(const char *string) {
  761. int num_newlines = count_letters(string, '\n', strlen(string));
  762. // if we just have one extra line, ignore the paging prompt and just send
  763. // the entire thing
  764. if(num_newlines <= NUM_LINES_PER_PAGE + 1)
  765. return 1;
  766. return ((num_newlines / NUM_LINES_PER_PAGE) +
  767. (num_newlines % NUM_LINES_PER_PAGE != 0));
  768. }
  769. void show_page(SOCKET_DATA *dsock, int page_num) {
  770. static char buf[MAX_BUFFER];
  771. char *page = dsock->page_string;
  772. *buf = '\0';
  773. int buf_i = 0, newline_count = 0, curr_page = 1;
  774. // skip to our page
  775. while(*page && curr_page < page_num) {
  776. if(*page == '\n')
  777. newline_count++;
  778. if(newline_count >= NUM_LINES_PER_PAGE) {
  779. curr_page++;
  780. newline_count = 0;
  781. }
  782. page++;
  783. }
  784. // copy our page to the buf
  785. while(*page && newline_count < NUM_LINES_PER_PAGE) {
  786. if(*page == '\n')
  787. newline_count++;
  788. buf[buf_i] = *page;
  789. page++;
  790. buf_i++;
  791. }
  792. buf[buf_i] = '\0';
  793. text_to_buffer(dsock, buf);
  794. if(dsock->tot_pages != 1)
  795. send_to_socket(dsock, "{c[{ypage %d of %d. Use MORE to view next page or BACK to view previous{c]{n\r\n",
  796. page_num, dsock->tot_pages);
  797. }
  798. void delete_page(SOCKET_DATA *dsock) {
  799. if(dsock->page_string) free(dsock->page_string);
  800. dsock->page_string = NULL;
  801. dsock->tot_pages = 0;
  802. dsock->curr_page = 0;
  803. }
  804. void page_string(SOCKET_DATA *dsock, const char *string) {
  805. if(dsock->page_string)
  806. free(dsock->page_string);
  807. dsock->page_string = strdup(string);
  808. dsock->tot_pages = count_pages(string);
  809. if(dsock->tot_pages == 1) {
  810. text_to_buffer(dsock, dsock->page_string);
  811. delete_page(dsock);
  812. }
  813. else {
  814. dsock->curr_page = 1;
  815. show_page(dsock, 1);
  816. }
  817. }
  818. void page_back(SOCKET_DATA *dsock) {
  819. dsock->curr_page--;
  820. if(dsock->tot_pages > 0 && dsock->curr_page <= dsock->tot_pages &&
  821. dsock->curr_page > 0)
  822. show_page(dsock, dsock->curr_page);
  823. else {
  824. dsock->curr_page = MIN(dsock->tot_pages, 1);
  825. text_to_buffer(dsock, "There is no more text in your page buffer.\r\n");
  826. }
  827. /*
  828. if(dsock->curr_page >= dsock->tot_pages)
  829. delete_page(dsock);
  830. */
  831. }
  832. void page_continue(SOCKET_DATA *dsock) {
  833. dsock->curr_page++;
  834. if(dsock->tot_pages > 0 && dsock->curr_page > 0 &&
  835. dsock->curr_page <= dsock->tot_pages)
  836. show_page(dsock, dsock->curr_page);
  837. else {
  838. dsock->curr_page = dsock->tot_pages;
  839. text_to_buffer(dsock, "There is no more text in your page buffer.\r\n");
  840. }
  841. }
  842. //
  843. // the command handler for the reader
  844. void read_handler(SOCKET_DATA *sock, char *input) {
  845. if(!strncasecmp(input, "more", strlen(input)))
  846. page_continue(sock);
  847. else if(!strncasecmp(input, "back", strlen(input)))
  848. page_back(sock);
  849. else if(!strncasecmp(input, "quit", strlen(input)))
  850. socketPopInputHandler(sock);
  851. else
  852. text_to_buffer(sock, "Invalid choice!\r\n");
  853. }
  854. //
  855. // the prompt for reading text
  856. void read_prompt(SOCKET_DATA *sock) {
  857. text_to_buffer(sock, "\r\nQ to stop reading> ");
  858. }
  859. //
  860. // a new handler that allows people to read long bits of text
  861. void start_reader(SOCKET_DATA *dsock, const char *text) {
  862. // add a new input handler to control the reading
  863. socketPushInputHandler(dsock, read_handler, read_prompt, "read text");
  864. // page the string
  865. page_string(dsock, text);
  866. }
  867. void do_copyover(void) {
  868. LIST_ITERATOR *sock_i = newListIterator(socket_list);
  869. SOCKET_DATA *sock = NULL;
  870. FILE *fp;
  871. char buf[100];
  872. char control_buf[20];
  873. char port_buf[20];
  874. if ((fp = fopen(COPYOVER_FILE, "w+")) == NULL)
  875. return;
  876. sprintf(buf, "\n\r <*> The world starts spinning <*>\n\r");
  877. // For each playing descriptor, save its character and account
  878. ITERATE_LIST(sock, sock_i) {
  879. compressEnd(sock, sock->compressing, FALSE);
  880. // kick off anyone who hasn't yet logged in a character
  881. if (!socketGetChar(sock) || !socketGetAccount(sock) ||
  882. !charGetRoom(socketGetChar(sock))) {
  883. text_to_socket(sock, "\r\nSorry, we are rebooting. Come back in a few minutes.\r\n");
  884. close_socket(sock, FALSE);
  885. }
  886. // save account and player info to file
  887. else {
  888. fprintf(fp, "%d %s %s %s\n",
  889. sock->control, accountGetName(sock->account),
  890. charGetName(sock->player), sock->hostname);
  891. // save the player
  892. save_player(sock->player);
  893. save_account(sock->account);
  894. text_to_socket(sock, buf);
  895. }
  896. } deleteListIterator(sock_i);
  897. fprintf (fp, "-1\n");
  898. fclose (fp);
  899. // close any pending sockets
  900. recycle_sockets();
  901. #ifdef MODULE_WEBSERVER
  902. // if we have a webserver set up, finalize that
  903. finalize_webserver();
  904. #endif
  905. // exec - descriptors are inherited
  906. sprintf(control_buf, "%d", control);
  907. sprintf(port_buf, "%d", mudport);
  908. execl(EXE_FILE, "NakedMud", "-copyover", control_buf, port_buf, NULL);
  909. }
  910. //*****************************************************************************
  911. // get and set functions
  912. //*****************************************************************************
  913. CHAR_DATA *socketGetChar ( SOCKET_DATA *dsock) {
  914. return dsock->player;
  915. }
  916. void socketSetChar ( SOCKET_DATA *dsock, CHAR_DATA *ch) {
  917. dsock->player = ch;
  918. }
  919. ACCOUNT_DATA *socketGetAccount ( SOCKET_DATA *dsock) {
  920. return dsock->account;
  921. }
  922. void socketSetAccount (SOCKET_DATA *dsock, ACCOUNT_DATA *account) {
  923. dsock->account = account;
  924. }
  925. BUFFER *socketGetTextEditor ( SOCKET_DATA *sock) {
  926. return sock->text_editor;
  927. }
  928. BUFFER *socketGetOutbound ( SOCKET_DATA *sock) {
  929. return sock->outbuf;
  930. }
  931. void socketPushInputHandler ( SOCKET_DATA *socket,
  932. void handler(SOCKET_DATA *socket, char *input),
  933. void prompt (SOCKET_DATA *socket),
  934. const char *state) {
  935. IH_PAIR *pair = malloc(sizeof(IH_PAIR));
  936. pair->handler = handler;
  937. pair->prompt = prompt;
  938. pair->python = FALSE;
  939. pair->state = strdupsafe(state);
  940. listPush(socket->input_handlers, pair);
  941. }
  942. void socketPushPyInputHandler(SOCKET_DATA *sock, void *handler,void *prompt,
  943. const char *state) {
  944. IH_PAIR *pair = malloc(sizeof(IH_PAIR));
  945. pair->handler = handler;
  946. pair->prompt = prompt;
  947. pair->python = TRUE;
  948. pair->state = strdupsafe(state);
  949. Py_XINCREF((PyObject *)handler);
  950. Py_XINCREF((PyObject *)prompt);
  951. listPush(sock->input_handlers, pair);
  952. }
  953. void socketReplacePyInputHandler(SOCKET_DATA *sock, void *handler,void *prompt,
  954. const char *state){
  955. socketPopInputHandler(sock);
  956. socketPushPyInputHandler(sock, handler, prompt, state);
  957. }
  958. const char *socketGetLastCmd(SOCKET_DATA *sock) {
  959. if(listSize(sock->command_hist) == 0)
  960. return "";
  961. else
  962. return listHead(sock->command_hist);
  963. }
  964. void socketPopInputHandler ( SOCKET_DATA *socket) {
  965. IH_PAIR *pair = listPop(socket->input_handlers);
  966. if(pair != NULL)
  967. deleteInputHandler(pair);
  968. }
  969. void socketReplaceInputHandler( SOCKET_DATA *socket,
  970. void handler(SOCKET_DATA *socket, char *input),
  971. void prompt (SOCKET_DATA *socket),
  972. const char *state) {
  973. socketPopInputHandler(socket);
  974. socketPushInputHandler(socket, handler, prompt, state);
  975. }
  976. void socketQueueCommand( SOCKET_DATA *sock, const char *cmd) {
  977. listQueue(sock->input, strdup(cmd));
  978. }
  979. bool socketHasCommand(SOCKET_DATA *sock) {
  980. return sock->cmd_read;
  981. }
  982. int socketGetUID( SOCKET_DATA *dsock) {
  983. return dsock->uid;
  984. }
  985. bool socketHasPrompt(SOCKET_DATA *sock) {
  986. IH_PAIR *pair = listGet(sock->input_handlers, 0);
  987. return (pair != NULL && pair->prompt != NULL);
  988. }
  989. void socketShowPrompt( SOCKET_DATA *sock) {
  990. IH_PAIR *pair = listGet(sock->input_handlers, 0);
  991. if(pair == NULL || pair->prompt == NULL)
  992. return;
  993. else if(pair->python == FALSE) {
  994. ((void (*)(SOCKET_DATA *))pair->prompt)(sock);
  995. }
  996. else {
  997. PyObject *arglist = Py_BuildValue("(O)", socketGetPyFormBorrowed(sock));
  998. PyObject *retval = PyEval_CallObject(pair->prompt, arglist);
  999. // check for an error:
  1000. if(retval == NULL)
  1001. log_pyerr("Error with a Python prompt");
  1002. // garbage collection
  1003. Py_XDECREF(retval);
  1004. Py_XDECREF(arglist);
  1005. }
  1006. }
  1007. void *socketGetAuxiliaryData ( SOCKET_DATA *sock, const char *name) {
  1008. return auxiliaryGet(sock->auxiliary, name);
  1009. }
  1010. const char *socketGetHostname(SOCKET_DATA *sock) {
  1011. return sock->hostname;
  1012. }
  1013. int socketGetDNSLookupStatus(SOCKET_DATA *sock) {
  1014. return sock->lookup_status;
  1015. }
  1016. void socketBustPrompt(SOCKET_DATA *sock) {
  1017. sock->bust_prompt = TRUE;
  1018. }
  1019. const char *socketGetState(SOCKET_DATA *sock) {
  1020. IH_PAIR *pair = listGet(sock->input_handlers, 0);
  1021. if(pair == NULL)
  1022. return "";
  1023. else
  1024. return pair->state;
  1025. }
  1026. double socketGetIdleTime(SOCKET_DATA *sock) {
  1027. return sock->idle;
  1028. }
  1029. //*****************************************************************************
  1030. // MCCP SUPPORT IS BELOW THIS LINE. NOTHING BUT MCCP SUPPORT SHOULD GO BELOW
  1031. // THIS LINE.
  1032. //*****************************************************************************
  1033. /*
  1034. * mccp.c - support functions for the Mud Client Compression Protocol
  1035. *
  1036. * see http://www.randomly.org/projects/MCCP/
  1037. *
  1038. * Copyright (c) 1999, Oliver Jowett <oliver@randomly.org>
  1039. *
  1040. * This code may be freely distributed and used if this copyright
  1041. * notice is retained intact.
  1042. */
  1043. /*
  1044. #include <stdio.h>
  1045. #include <string.h>
  1046. #include <stdlib.h>
  1047. #include <unistd.h>
  1048. #include "mud.h"
  1049. #include "socket.h"
  1050. #include "utils.h"
  1051. */
  1052. /* local functions */
  1053. bool processCompressed ( SOCKET_DATA *dsock );
  1054. const unsigned char enable_compress [] = { IAC, SB, TELOPT_COMPRESS, WILL, SE, 0 };
  1055. const unsigned char enable_compress2 [] = { IAC, SB, TELOPT_COMPRESS2, IAC, SE, 0 };
  1056. /*
  1057. * Memory management - zlib uses these hooks to allocate and free memory
  1058. * it needs
  1059. */
  1060. void *zlib_alloc(void *opaque, unsigned int items, unsigned int size)
  1061. {
  1062. return calloc(items, size);
  1063. }
  1064. void zlib_free(void *opaque, void *address)
  1065. {
  1066. free(address);
  1067. }
  1068. /*
  1069. * Begin compressing data on `desc'
  1070. */
  1071. bool compressStart(SOCKET_DATA *dsock, unsigned char teleopt)
  1072. {
  1073. z_stream *s;
  1074. /* already compressing */
  1075. if (dsock->out_compress)
  1076. return TRUE;
  1077. /* allocate and init stream, buffer */
  1078. s = (z_stream *) malloc(sizeof(*s));
  1079. dsock->out_compress_buf = (unsigned char *) malloc(COMPRESS_BUF_SIZE);
  1080. s->next_in = NULL;
  1081. s->avail_in = 0;
  1082. s->next_out = dsock->out_compress_buf;
  1083. s->avail_out = COMPRESS_BUF_SIZE;
  1084. s->zalloc = zlib_alloc;
  1085. s->zfree = zlib_free;
  1086. s->opaque = NULL;
  1087. if (deflateInit(s, 9) != Z_OK)
  1088. {
  1089. free(dsock->out_compress_buf);
  1090. free(s);
  1091. return FALSE;
  1092. }
  1093. /* version 1 or 2 support */
  1094. if (teleopt == TELOPT_COMPRESS)
  1095. text_to_socket(dsock, (char *) enable_compress);
  1096. else if (teleopt == TELOPT_COMPRESS2)
  1097. text_to_socket(dsock, (char *) enable_compress2);
  1098. else
  1099. {
  1100. bug("Bad teleoption %d passed", teleopt);
  1101. free(dsock->out_compress_buf);
  1102. free(s);
  1103. return FALSE;
  1104. }
  1105. /* now we're compressing */
  1106. dsock->compressing = teleopt;
  1107. dsock->out_compress = s;
  1108. /* success */
  1109. return TRUE;
  1110. }
  1111. /* Cleanly shut down compression on `desc' */
  1112. bool compressEnd(SOCKET_DATA *dsock, unsigned char teleopt, bool forced)
  1113. {
  1114. unsigned char dummy[1];
  1115. if (!dsock->out_compress)
  1116. return TRUE;
  1117. if (dsock->compressing != teleopt)
  1118. return FALSE;
  1119. dsock->out_compress->avail_in = 0;
  1120. dsock->out_compress->next_in = dummy;
  1121. /* No terminating signature is needed - receiver will get Z_STREAM_END */
  1122. if (deflate(dsock->out_compress, Z_FINISH) != Z_STREAM_END && !forced)
  1123. return FALSE;
  1124. /* try to send any residual data */
  1125. if (!processCompressed(dsock) && !forced)
  1126. return FALSE;
  1127. /* reset compression values */
  1128. deflateEnd(dsock->out_compress);
  1129. free(dsock->out_compress_buf);
  1130. free(dsock->out_compress);
  1131. dsock->compressing = 0;
  1132. dsock->out_compress = NULL;
  1133. dsock->out_compress_buf = NULL;
  1134. /* success */
  1135. return TRUE;
  1136. }
  1137. /* Try to send any pending compressed-but-not-sent data in `desc' */
  1138. bool processCompressed(SOCKET_DATA *dsock)
  1139. {
  1140. int iStart, nBlock, nWrite, len;
  1141. if (!dsock->out_compress)
  1142. return TRUE;
  1143. len = dsock->out_compress->next_out - dsock->out_compress_buf;
  1144. if (len > 0)
  1145. {
  1146. for (iStart = 0; iStart < len; iStart += nWrite)
  1147. {
  1148. nBlock = UMIN (len - iStart, 4096);
  1149. if ((nWrite = write(dsock->control, dsock->out_compress_buf + iStart, nBlock)) < 0)
  1150. {
  1151. if (errno == EAGAIN/* || errno == ENOSR*/)
  1152. break;
  1153. /* write error */
  1154. return FALSE;
  1155. }
  1156. if (nWrite <= 0)
  1157. break;
  1158. }
  1159. if (iStart)
  1160. {
  1161. if (iStart < len)
  1162. memmove(dsock->out_compress_buf, dsock->out_compress_buf+iStart, len - iStart);
  1163. dsock->out_compress->next_out = dsock->out_compress_buf + len - iStart;
  1164. }
  1165. }
  1166. /* success */
  1167. return TRUE;
  1168. }
  1169. //
  1170. // compress output
  1171. //
  1172. COMMAND(cmd_compress)
  1173. {
  1174. /* no socket, no compression */
  1175. if (!charGetSocket(ch))
  1176. return;
  1177. /* enable compression */
  1178. if (!charGetSocket(ch)->out_compress) {
  1179. text_to_char(ch, "Trying compression.\n\r");
  1180. text_to_buffer(charGetSocket(ch), (char *) compress_will2);
  1181. text_to_buffer(charGetSocket(ch), (char *) compress_will);
  1182. }
  1183. else /* disable compression */ {
  1184. if (!compressEnd(charGetSocket(ch), charGetSocket(ch)->compressing, FALSE)){
  1185. text_to_char(ch, "Failed.\n\r");
  1186. return;
  1187. }
  1188. text_to_char(ch, "Compression disabled.\n\r");
  1189. }
  1190. }
  1191. //*****************************************************************************
  1192. // IF YOU ARE PUTTING ANYTHING BELOW THIS LINE, YOU ARE PUTTING IT IN THE WRONG
  1193. // PLACE!! ALL SOCKET-RELATED STUFF SHOULD GO UP ABOVE THE MCCP SUPPORT STUFF!
  1194. //*****************************************************************************