Mercurial > hg > nnchat
annotate nnchat.c @ 25:3b67a9a806a7
Added /color command to change colour.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Wed, 09 Jul 2008 17:47:40 +0300 |
parents | 78d260256450 |
children | b84fc46c6035 |
rev | line source |
---|---|
0 | 1 #ifdef __WIN32 |
2 #include <winsock2.h> | |
3 #else | |
4 #include <sys/socket.h> | |
5 #include <sys/types.h> | |
6 #include <arpa/inet.h> | |
7 #include <sys/time.h> | |
8 #include <netdb.h> | |
9 #endif | |
10 | |
11 #include <unistd.h> | |
12 #include <stdlib.h> | |
13 #include <stdio.h> | |
14 #include "th_args.h" | |
15 #include "th_string.h" | |
16 #include <string.h> | |
17 #include <errno.h> | |
18 #include <time.h> | |
19 | |
20 | |
21 #define SET_ALLOC_SIZE (128) | |
22 #define SET_SELECT_USEC (100000) | |
23 | |
24 | |
25 /* Options | |
26 */ | |
27 int optPort = 8005; | |
28 int optUserColor = 0x408060; | |
29 char *optServer = "www11.servemedata.com", | |
30 *optUserName = NULL, | |
31 *optUserName2 = NULL, | |
32 *optPassword = NULL, | |
24
78d260256450
Added two simple commands to simplify private chatting, "/to" and "/msg".
Matti Hamalainen <ccr@tnsp.org>
parents:
23
diff
changeset
|
33 *optLogFilename = NULL, |
78d260256450
Added two simple commands to simplify private chatting, "/to" and "/msg".
Matti Hamalainen <ccr@tnsp.org>
parents:
23
diff
changeset
|
34 *setTarget = NULL; |
21 | 35 BOOL optDaemon = FALSE; |
0 | 36 FILE *optLogFile = NULL; |
37 | |
38 | |
39 /* Arguments | |
40 */ | |
41 optarg_t optList[] = { | |
42 { 0, '?', "help", "Show this help", OPT_NONE }, | |
43 { 1, 'v', "verbose", "Be more verbose", OPT_NONE }, | |
44 { 2, 'p', "port", "Connect to port", OPT_ARGREQ }, | |
45 { 3, 's', "server", "Server to connect to", OPT_ARGREQ }, | |
46 { 4, 'C', "color", "Initial color in RGB hex 000000", OPT_ARGREQ }, | |
47 { 5, 'l', "logfile", "Log filename", OPT_ARGREQ }, | |
21 | 48 { 6, 'D', "daemon", "A pseudo-daemon mode for logging", OPT_NONE }, |
0 | 49 }; |
50 | |
51 const int optListN = (sizeof(optList) / sizeof(optarg_t)); | |
52 | |
53 | |
54 void argShowHelp() | |
55 { | |
56 th_args_help(stdout, optList, optListN, th_prog_name, | |
57 "[options] <username> <password>"); | |
58 } | |
59 | |
60 | |
61 #ifdef __WIN32 | |
1 | 62 /* Just a bogus stub |
63 */ | |
0 | 64 const char *hstrerror(int err) |
65 { | |
1 | 66 (void) err; |
67 | |
0 | 68 return "???"; |
69 } | |
70 #endif | |
71 | |
72 BOOL argHandleOpt(const int optN, char *optArg, char *currArg) | |
73 { | |
74 switch (optN) { | |
75 case 0: | |
76 argShowHelp(); | |
77 exit(0); | |
78 break; | |
79 | |
80 case 1: | |
81 th_verbosityLevel++; | |
82 break; | |
83 | |
84 case 2: | |
85 optPort = atoi(optArg); | |
86 break; | |
87 | |
88 case 3: | |
89 optServer = optArg; | |
90 break; | |
91 | |
92 case 4: | |
93 if (sscanf(optArg, "%06x", &optUserColor) != 1) { | |
94 THERR("Invalid color argument '%s', should be a RGB hex triplet '000000'.\n", | |
95 optArg); | |
96 return FALSE; | |
97 } | |
98 THMSG(1, "Using color #%06x\n", optUserColor); | |
99 break; | |
100 | |
101 case 5: | |
102 optLogFilename = optArg; | |
103 break; | |
104 | |
21 | 105 case 6: |
106 optDaemon = TRUE; | |
107 THMSG(1, "Running in pseudo-daemon mode.\n"); | |
108 break; | |
109 | |
0 | 110 default: |
111 THERR("Unknown option '%s'.\n", currArg); | |
112 return FALSE; | |
113 } | |
114 | |
115 return TRUE; | |
116 } | |
117 | |
118 | |
119 BOOL argHandleFile(char *currArg) | |
120 { | |
121 if (!optUserName) | |
122 optUserName = currArg; | |
123 else if (!optPassword) | |
124 optPassword = currArg; | |
125 else { | |
126 THERR("Username '%s' already specified on commandline!\n", optUserName); | |
127 return FALSE; | |
128 } | |
129 | |
130 return TRUE; | |
131 } | |
132 | |
133 | |
15 | 134 int openConnection(struct in_addr *addr, int port) |
135 { | |
136 struct sockaddr_in tmpAddr; | |
137 int sock = -1; | |
138 | |
139 tmpAddr.sin_family = AF_INET; | |
140 tmpAddr.sin_port = htons(port); | |
141 tmpAddr.sin_addr = *addr; | |
142 | |
143 THMSG(1, "Connecting to %s:%d ...\n", | |
144 inet_ntoa(tmpAddr.sin_addr), port); | |
145 | |
146 if ((sock = socket(PF_INET, SOCK_STREAM, 0)) == -1) { | |
147 THERR("Could not open socket: %s\n", strerror(errno)); | |
148 return -2; | |
149 } | |
150 | |
151 THMSG(2, "Using socket %d.\n", sock); | |
152 | |
153 if (connect(sock, (struct sockaddr *) &tmpAddr, sizeof(tmpAddr)) == -1) { | |
154 THERR("Could not connect: %s\n", strerror(errno)); | |
155 return -5; | |
156 } | |
157 | |
158 return sock; | |
159 } | |
160 | |
161 | |
162 void closeConnection(int sock) | |
163 { | |
164 if (sock >= 0) { | |
165 #ifdef __WIN32 | |
166 closesocket(sock); | |
167 #else | |
168 close(sock); | |
169 #endif | |
170 } | |
171 } | |
172 | |
173 | |
0 | 174 BOOL sendToSocket(int sock, char *buf, const size_t bufLen) |
175 { | |
176 size_t bufLeft = bufLen; | |
177 char *bufPtr = buf; | |
178 | |
179 while (bufLeft > 0) { | |
180 ssize_t bufSent; | |
181 bufSent = send(sock, bufPtr, bufLeft, 0); | |
182 if (bufSent < 0) return FALSE; | |
183 bufLeft -= bufSent; | |
184 bufPtr += bufSent; | |
185 } | |
186 return TRUE; | |
187 } | |
188 | |
189 | |
190 void printMsg(char *fmt, ...) | |
191 { | |
192 char tmpStr[64] = ""; | |
193 va_list ap; | |
194 time_t timeStamp; | |
195 struct tm *tmpTime;; | |
196 | |
197 timeStamp = time(NULL); | |
198 if ((tmpTime = localtime(&timeStamp)) != NULL) { | |
199 strftime(tmpStr, sizeof(tmpStr), "[%H:%M:%S] ", tmpTime); | |
200 } | |
201 | |
202 if (optLogFile) { | |
203 fputs(tmpStr, optLogFile); | |
204 va_start(ap, fmt); | |
205 vfprintf(optLogFile, fmt, ap); | |
206 va_end(ap); | |
207 fflush(optLogFile); | |
208 } | |
209 | |
21 | 210 if (!optDaemon) { |
211 fputs(tmpStr, stdout); | |
212 va_start(ap, fmt); | |
213 vfprintf(stdout, fmt, ap); | |
214 va_end(ap); | |
215 fflush(stdout); | |
216 } | |
0 | 217 } |
218 | |
219 | |
220 BOOL bufRealloc(char **buf, size_t *size, size_t add) | |
221 { | |
222 return ((*buf = th_realloc(*buf, *size + add)) != NULL); | |
223 } | |
224 | |
12 | 225 #define PUSHCHAR(x) bufPushChar(&result, &resSize, &resPos, x) |
0 | 226 BOOL bufPushChar(char **buf, size_t *size, size_t *pos, char ch) |
227 { | |
228 if (*pos >= *size && !bufRealloc(buf, size, SET_ALLOC_SIZE)) | |
229 return FALSE; | |
230 | |
231 (*buf)[*pos] = ch; | |
232 (*pos)++; | |
233 return TRUE; | |
234 } | |
235 | |
12 | 236 #define PUSHSTR(x) bufPushStr(&result, &resSize, &resPos, x) |
0 | 237 BOOL bufPushStr(char **buf, size_t *size, size_t *pos, char *str) |
238 { | |
239 size_t tmpLen; | |
240 | |
241 if (!str) return FALSE; | |
242 tmpLen = strlen(str); | |
243 | |
244 if ((*pos + tmpLen) >= *size && !bufRealloc(buf, size, tmpLen + SET_ALLOC_SIZE)) | |
245 return FALSE; | |
246 | |
247 strcpy(*buf + *pos, str); | |
248 (*pos) += tmpLen; | |
249 return TRUE; | |
250 } | |
251 | |
252 | |
253 char *encodeStr1(char *str) | |
254 { | |
255 char *result, *s = str; | |
256 size_t resSize, resPos = 0; | |
257 | |
258 if (!str) return NULL; | |
259 | |
260 resSize = strlen(str) + SET_ALLOC_SIZE; | |
261 if ((result = th_malloc(resSize)) == NULL) | |
262 return NULL; | |
263 | |
264 while (*s) { | |
265 switch (*s) { | |
266 case 32: | |
12 | 267 PUSHCHAR('+'); |
0 | 268 break; |
269 | |
270 default: | |
271 if (th_isalnum(*s)) | |
12 | 272 PUSHCHAR(*s); |
0 | 273 else { |
274 char tmpStr[4]; | |
275 sprintf(tmpStr, "%2X", (unsigned char) *s); | |
12 | 276 PUSHCHAR('%'); |
277 PUSHSTR(tmpStr); | |
0 | 278 } |
279 break; | |
280 } | |
281 s++; | |
282 } | |
12 | 283 PUSHCHAR(0); |
0 | 284 |
285 return result; | |
286 } | |
287 | |
15 | 288 |
0 | 289 int getxdigit(int c, int shift) |
290 { | |
291 int i; | |
292 | |
293 if (c >= 'A' && c <= 'F') | |
294 i = c - 'A' + 10; | |
295 else if (c >= 'a' && c <= 'f') | |
296 i = c - 'a' + 10; | |
297 else if (c >= '0' && c <= '9') | |
298 i = c - '0'; | |
299 else | |
300 return -1; | |
301 | |
302 return i << shift; | |
303 } | |
304 | |
15 | 305 |
0 | 306 char *decodeStr1(char *str) |
307 { | |
308 char *result, *s = str; | |
309 size_t resSize, resPos = 0; | |
310 int c; | |
311 | |
312 if (!str) return NULL; | |
313 | |
314 resSize = strlen(str) + SET_ALLOC_SIZE; | |
315 if ((result = th_malloc(resSize)) == NULL) | |
316 return NULL; | |
317 | |
318 while (*s) { | |
319 switch (*s) { | |
320 case '+': | |
12 | 321 PUSHCHAR(' '); |
0 | 322 s++; |
323 break; | |
324 | |
325 case '%': | |
326 s++; | |
327 if (*s == '%') | |
12 | 328 PUSHCHAR('%'); |
0 | 329 else if ((c = getxdigit(*s, 4)) >= 0) { |
330 int i = getxdigit(*(++s), 0); | |
331 if (i >= 0) { | |
12 | 332 PUSHCHAR(c | i); |
0 | 333 } else { |
12 | 334 PUSHCHAR('§'); |
335 PUSHCHAR(*s); | |
0 | 336 } |
337 } else { | |
12 | 338 PUSHCHAR('§'); |
339 PUSHCHAR(*s); | |
0 | 340 } |
341 s++; | |
342 break; | |
343 | |
344 default: | |
12 | 345 PUSHCHAR(*s); |
0 | 346 s++; |
347 } | |
348 } | |
12 | 349 PUSHCHAR(0); |
0 | 350 |
351 return result; | |
352 } | |
353 | |
354 | |
355 char *stripTags(char *str) | |
356 { | |
357 char *result, *s = str; | |
358 size_t resSize, resPos = 0; | |
359 | |
360 if (!str) return NULL; | |
361 | |
362 resSize = strlen(str) + SET_ALLOC_SIZE; | |
363 if ((result = th_malloc(resSize)) == NULL) | |
364 return NULL; | |
365 | |
366 while (*s) { | |
367 if (*s == '<') { | |
368 while (*s && *s != '>') s++; | |
369 if (*s == '>') s++; | |
370 } else | |
12 | 371 PUSHCHAR(*s++); |
0 | 372 } |
12 | 373 PUSHCHAR(0); |
0 | 374 |
375 return result; | |
376 } | |
377 | |
378 | |
379 typedef struct { | |
380 char c; | |
381 char *ent; | |
382 } html_entity_t; | |
383 | |
384 | |
385 html_entity_t HTMLEntities[] = { | |
386 { '<', "<" }, | |
387 { '>', ">" }, | |
388 }; | |
389 | |
390 const int numHTMLEntities = (sizeof(HTMLEntities) / sizeof(html_entity_t)); | |
391 | |
392 | |
393 char *encodeStr2(char *str) | |
394 { | |
395 char *result, *s = str; | |
396 size_t resSize, resPos = 0; | |
397 | |
398 if (!str) return NULL; | |
399 | |
400 resSize = strlen(str) + SET_ALLOC_SIZE; | |
401 if ((result = th_malloc(resSize)) == NULL) | |
402 return NULL; | |
403 | |
404 while (*s) { | |
405 int i; | |
406 BOOL found = FALSE; | |
407 for (i = 0; i < numHTMLEntities; i++) | |
408 if (HTMLEntities[i].c == *s) { | |
12 | 409 PUSHSTR(HTMLEntities[i].ent); |
0 | 410 found = TRUE; |
411 break; | |
412 } | |
12 | 413 if (!found) PUSHCHAR(*s); |
0 | 414 |
415 s++; | |
416 } | |
12 | 417 PUSHCHAR(0); |
0 | 418 |
419 return result; | |
420 } | |
421 | |
422 | |
423 char *decodeStr2(char *str) | |
424 { | |
425 char *result, *s = str; | |
426 size_t resSize, resPos = 0; | |
427 | |
428 if (!str) return NULL; | |
429 | |
430 resSize = strlen(str); | |
431 if ((result = th_malloc(resSize)) == NULL) | |
432 return NULL; | |
433 | |
434 while (*s) { | |
435 if (*s == '&') { | |
436 int i; | |
437 BOOL found = FALSE; | |
438 for (i = 0; i < numHTMLEntities; i++) { | |
439 html_entity_t *ent = &HTMLEntities[i]; | |
440 int len = strlen(ent->ent); | |
441 if (!strncmp(s, ent->ent, len)) { | |
12 | 442 PUSHCHAR(ent->c); |
0 | 443 s += len; |
444 found = TRUE; | |
445 break; | |
446 } | |
447 } | |
12 | 448 if (!found) PUSHCHAR(*s++); |
0 | 449 } else |
12 | 450 PUSHCHAR(*s++); |
0 | 451 } |
12 | 452 PUSHCHAR(0); |
0 | 453 |
454 return result; | |
455 } | |
456 | |
457 | |
458 BOOL sendUserMsg(int sock, char *user, char *fmt, ...) | |
459 { | |
460 char tmpBuf[4096], tmpBuf2[4096+256]; | |
461 int n; | |
462 va_list ap; | |
463 | |
464 va_start(ap, fmt); | |
465 n = vsnprintf(tmpBuf, sizeof(tmpBuf), fmt, ap); | |
466 va_end(ap); | |
467 | |
468 if (n < 0) return FALSE; | |
469 | |
470 snprintf(tmpBuf2, sizeof(tmpBuf2), | |
471 "<USER>%s</USER><MESSAGE>%s</MESSAGE>", | |
472 user, tmpBuf); | |
473 | |
474 return sendToSocket(sock, tmpBuf2, strlen(tmpBuf2) + 1); | |
475 } | |
476 | |
477 | |
478 int handleUser(int sock, char *str) | |
479 { | |
480 const char *msg = "</USER><MESSAGE>"; | |
22 | 481 char *p = str, *q, *s, *t, *h; |
0 | 482 |
483 (void) sock; | |
484 | |
485 s = strstr(str, msg); | |
486 if (!s) return 1; | |
487 *s = 0; | |
488 s += strlen(msg); | |
489 | |
490 q = strstr(s, "</MESSAGE>"); | |
491 if (!q) return 3; | |
492 *q = 0; | |
493 | |
494 s = decodeStr1(s); | |
495 if (!s) return -1; | |
496 | |
497 p = decodeStr1(p); | |
498 if (!p) { | |
499 th_free(s); | |
500 return -2; | |
501 } | |
502 | |
503 | |
504 if (*s == '/') { | |
19 | 505 if (!strncmp(s, "/BPRV", 5)) { |
506 t = stripTags(s + 2); | |
22 | 507 h = decodeStr2(t); |
508 printMsg("%s\n", h); | |
19 | 509 } else { |
510 t = stripTags(s + 1); | |
22 | 511 h = decodeStr2(t); |
512 printMsg("* %s\n", h); | |
19 | 513 } |
22 | 514 th_free(h); |
0 | 515 th_free(t); |
516 } else { | |
22 | 517 t = stripTags(s); |
518 h = decodeStr2(t); | |
519 printMsg("<%s> %s\n", p, h); | |
520 th_free(h); | |
0 | 521 th_free(t); |
522 } | |
523 | |
524 th_free(s); | |
525 th_free(p); | |
526 return 0; | |
527 } | |
528 | |
529 | |
530 int handleLogin(int sock, char *str) | |
531 { | |
19 | 532 char tmpStr[256] = ""; |
533 time_t timeStamp; | |
534 struct tm *tmpTime;; | |
535 | |
536 timeStamp = time(NULL); | |
537 if ((tmpTime = localtime(&timeStamp)) != NULL) { | |
538 strftime(tmpStr, sizeof(tmpStr), "%c", tmpTime); | |
539 } | |
540 | |
0 | 541 if (!strncmp(str, "FAILURE", 7)) { |
19 | 542 printMsg("Login failure - %s\n", tmpStr); |
0 | 543 return -2; |
544 } else if (!strncmp(str, "SUCCESS", 7)) { | |
19 | 545 printMsg("Login success - %s\n", tmpStr); |
13
86fe5f0d1a85
Cleanups; Added probing connection (requesting some policy crap) to emulate the official client.
Matti Hamalainen <ccr@tnsp.org>
parents:
12
diff
changeset
|
546 sendUserMsg(sock, optUserName2, "%%2FRequestUserList"); |
0 | 547 return 0; |
548 } else | |
549 return 1; | |
550 } | |
551 | |
552 | |
553 int handleAddUser(int sock, char *str) | |
554 { | |
9 | 555 char *p, *s = strstr(str, "</ADD_USER>"); |
0 | 556 |
557 (void) sock; | |
558 | |
559 if (!s) return 1; | |
560 *s = 0; | |
9 | 561 |
562 p = decodeStr1(str); | |
563 if (!p) return -1; | |
564 | |
565 printMsg("! %s ADDED.\n", p); | |
566 th_free(p); | |
0 | 567 return 0; |
568 } | |
569 | |
570 | |
571 int handleDeleteUser(int sock, char *str) | |
572 { | |
9 | 573 char *p, *s = strstr(str, "</DELETE_USER>"); |
0 | 574 |
575 (void) sock; | |
576 | |
577 if (!s) return 1; | |
578 *s = 0; | |
9 | 579 |
580 p = decodeStr1(str); | |
581 if (!p) return -1; | |
582 | |
583 printMsg("! %s DELETED.\n", p); | |
584 th_free(p); | |
0 | 585 return 0; |
586 } | |
587 | |
588 | |
589 int handleFoo(int sock, char *str) | |
590 { | |
591 (void) sock; (void) str; | |
592 | |
593 return 0; | |
594 } | |
595 | |
596 | |
597 typedef struct { | |
598 char *cmd; | |
599 int (*handler)(int, char *); | |
600 } protocmd_t; | |
601 | |
602 | |
603 protocmd_t protoCmds[] = { | |
604 { "<USER>", handleUser }, | |
605 { "<LOGIN_", handleLogin }, | |
606 { "<DELETE_USER>", handleDeleteUser }, | |
607 { "<ADD_USER>", handleAddUser }, | |
608 { "<NUMCLIENTS>", handleFoo }, | |
609 }; | |
610 | |
611 const int nprotoCmds = (sizeof(protoCmds) / sizeof(protocmd_t)); | |
612 | |
613 | |
614 int handleProtocol(int sock, char *buf, size_t bufLen) | |
615 { | |
616 int i; | |
617 | |
618 for (i = 0; i < nprotoCmds; i++) { | |
619 size_t cmdLen = strlen(protoCmds[i].cmd); | |
620 if (cmdLen < bufLen && !strncmp(buf, protoCmds[i].cmd, cmdLen)) { | |
621 return protoCmds[i].handler(sock, buf + cmdLen); | |
622 } | |
623 } | |
624 | |
625 return 1; | |
626 } | |
627 | |
628 | |
15 | 629 int handleUserInput(int sock, char *buf, size_t bufLen) |
0 | 630 { |
25
3b67a9a806a7
Added /color command to change colour.
Matti Hamalainen <ccr@tnsp.org>
parents:
24
diff
changeset
|
631 char *tmpStr, *tmpStr2, tmpBuf[4096]; |
0 | 632 BOOL result; |
633 | |
634 /* Trim right */ | |
635 buf[--bufLen] = 0; | |
636 while (bufLen > 0 && (buf[bufLen] == '\n' || buf[bufLen] == '\r' || th_isspace(buf[bufLen]))) | |
637 buf[bufLen--] = 0; | |
638 | |
639 //fprintf(stderr, "'%s'\n", buf); fflush(stderr); | |
640 | |
641 /* Check command */ | |
642 if (*buf == 0) { | |
643 return 1; | |
25
3b67a9a806a7
Added /color command to change colour.
Matti Hamalainen <ccr@tnsp.org>
parents:
24
diff
changeset
|
644 } else if (!strncmp(buf, "/color ", 7)) { |
3b67a9a806a7
Added /color command to change colour.
Matti Hamalainen <ccr@tnsp.org>
parents:
24
diff
changeset
|
645 if (sscanf(buf+7, "%6x", &optUserColor) != 1) { |
3b67a9a806a7
Added /color command to change colour.
Matti Hamalainen <ccr@tnsp.org>
parents:
24
diff
changeset
|
646 printMsg("Invalid color value '%s'\n", buf+7); |
3b67a9a806a7
Added /color command to change colour.
Matti Hamalainen <ccr@tnsp.org>
parents:
24
diff
changeset
|
647 return 1; |
3b67a9a806a7
Added /color command to change colour.
Matti Hamalainen <ccr@tnsp.org>
parents:
24
diff
changeset
|
648 } |
3b67a9a806a7
Added /color command to change colour.
Matti Hamalainen <ccr@tnsp.org>
parents:
24
diff
changeset
|
649 printMsg("Setting color to %06X", optUserColor); |
3b67a9a806a7
Added /color command to change colour.
Matti Hamalainen <ccr@tnsp.org>
parents:
24
diff
changeset
|
650 sendUserMsg(sock, optUserName2, "%%2FSetFontColor%%20%%2Dcolor%%20%06X", optUserColor); |
3b67a9a806a7
Added /color command to change colour.
Matti Hamalainen <ccr@tnsp.org>
parents:
24
diff
changeset
|
651 return 0; |
24
78d260256450
Added two simple commands to simplify private chatting, "/to" and "/msg".
Matti Hamalainen <ccr@tnsp.org>
parents:
23
diff
changeset
|
652 } else if (!strncmp(buf, "/msg ", 5)) { |
78d260256450
Added two simple commands to simplify private chatting, "/to" and "/msg".
Matti Hamalainen <ccr@tnsp.org>
parents:
23
diff
changeset
|
653 if (setTarget) { |
78d260256450
Added two simple commands to simplify private chatting, "/to" and "/msg".
Matti Hamalainen <ccr@tnsp.org>
parents:
23
diff
changeset
|
654 snprintf(tmpBuf, sizeof(tmpBuf), "/prv -to %s -msg %s", setTarget, buf+5); |
78d260256450
Added two simple commands to simplify private chatting, "/to" and "/msg".
Matti Hamalainen <ccr@tnsp.org>
parents:
23
diff
changeset
|
655 buf = tmpBuf; |
78d260256450
Added two simple commands to simplify private chatting, "/to" and "/msg".
Matti Hamalainen <ccr@tnsp.org>
parents:
23
diff
changeset
|
656 } else { |
25
3b67a9a806a7
Added /color command to change colour.
Matti Hamalainen <ccr@tnsp.org>
parents:
24
diff
changeset
|
657 printMsg("No target set!\n"); |
24
78d260256450
Added two simple commands to simplify private chatting, "/to" and "/msg".
Matti Hamalainen <ccr@tnsp.org>
parents:
23
diff
changeset
|
658 return 1; |
78d260256450
Added two simple commands to simplify private chatting, "/to" and "/msg".
Matti Hamalainen <ccr@tnsp.org>
parents:
23
diff
changeset
|
659 } |
78d260256450
Added two simple commands to simplify private chatting, "/to" and "/msg".
Matti Hamalainen <ccr@tnsp.org>
parents:
23
diff
changeset
|
660 } else if (!strncmp(buf, "/to ", 4)) { |
78d260256450
Added two simple commands to simplify private chatting, "/to" and "/msg".
Matti Hamalainen <ccr@tnsp.org>
parents:
23
diff
changeset
|
661 buf += 4; |
78d260256450
Added two simple commands to simplify private chatting, "/to" and "/msg".
Matti Hamalainen <ccr@tnsp.org>
parents:
23
diff
changeset
|
662 th_free(setTarget); |
78d260256450
Added two simple commands to simplify private chatting, "/to" and "/msg".
Matti Hamalainen <ccr@tnsp.org>
parents:
23
diff
changeset
|
663 setTarget = th_strdup(buf); |
25
3b67a9a806a7
Added /color command to change colour.
Matti Hamalainen <ccr@tnsp.org>
parents:
24
diff
changeset
|
664 printMsg("Set prv target to '%s'\n", setTarget); |
24
78d260256450
Added two simple commands to simplify private chatting, "/to" and "/msg".
Matti Hamalainen <ccr@tnsp.org>
parents:
23
diff
changeset
|
665 return 0; |
78d260256450
Added two simple commands to simplify private chatting, "/to" and "/msg".
Matti Hamalainen <ccr@tnsp.org>
parents:
23
diff
changeset
|
666 } |
78d260256450
Added two simple commands to simplify private chatting, "/to" and "/msg".
Matti Hamalainen <ccr@tnsp.org>
parents:
23
diff
changeset
|
667 |
78d260256450
Added two simple commands to simplify private chatting, "/to" and "/msg".
Matti Hamalainen <ccr@tnsp.org>
parents:
23
diff
changeset
|
668 { |
0 | 669 /* Send double-encoded */ |
22 | 670 //printf("ENC>%s\n", buf); |
671 //fflush(stdout); | |
0 | 672 |
673 tmpStr = encodeStr2(buf); | |
674 if (!tmpStr) return -2; | |
675 tmpStr2 = encodeStr1(tmpStr); | |
676 if (!tmpStr2) { | |
677 th_free(tmpStr); | |
678 return -3; | |
679 } | |
680 | |
681 result = sendUserMsg(sock, optUserName2, "%s", tmpStr2); | |
682 th_free(tmpStr); | |
683 th_free(tmpStr2); | |
684 if (result) | |
685 return 0; | |
686 else | |
687 return -1; | |
688 } | |
689 } | |
690 | |
691 | |
692 int main(int argc, char *argv[]) | |
693 { | |
694 int tmpSocket; | |
695 struct hostent *tmpHost; | |
13
86fe5f0d1a85
Cleanups; Added probing connection (requesting some policy crap) to emulate the official client.
Matti Hamalainen <ccr@tnsp.org>
parents:
12
diff
changeset
|
696 BOOL exitProg = FALSE, colorSet = FALSE; |
86fe5f0d1a85
Cleanups; Added probing connection (requesting some policy crap) to emulate the official client.
Matti Hamalainen <ccr@tnsp.org>
parents:
12
diff
changeset
|
697 struct timeval tv; |
86fe5f0d1a85
Cleanups; Added probing connection (requesting some policy crap) to emulate the official client.
Matti Hamalainen <ccr@tnsp.org>
parents:
12
diff
changeset
|
698 fd_set sockfds; |
86fe5f0d1a85
Cleanups; Added probing connection (requesting some policy crap) to emulate the official client.
Matti Hamalainen <ccr@tnsp.org>
parents:
12
diff
changeset
|
699 fd_set inputfds; |
86fe5f0d1a85
Cleanups; Added probing connection (requesting some policy crap) to emulate the official client.
Matti Hamalainen <ccr@tnsp.org>
parents:
12
diff
changeset
|
700 char *tmpStr; |
0 | 701 |
702 /* Initialize */ | |
21 | 703 th_init("NNChat", "Newbie Nudes chat client", "0.4", |
6
526ba3b578d7
Changed copyright etc. again.
Matti Hamalainen <ccr@tnsp.org>
parents:
4
diff
changeset
|
704 "Written and designed by Anonymous Finnish Guy (C) 2008", |
526ba3b578d7
Changed copyright etc. again.
Matti Hamalainen <ccr@tnsp.org>
parents:
4
diff
changeset
|
705 "This software is freeware, use and distribute as you wish."); |
0 | 706 th_verbosityLevel = 0; |
707 | |
708 /* Parse arguments */ | |
709 th_args_process(argc, argv, optList, optListN, | |
710 argHandleOpt, argHandleFile, FALSE); | |
711 | |
712 /* Check the mode and arguments */ | |
713 if (optUserName == NULL || optPassword == NULL) { | |
714 THERR("User/pass not specified, get some --help\n"); | |
715 return -1; | |
716 } | |
717 | |
718 /* Open logfile */ | |
719 if (optLogFilename) { | |
720 THMSG(1, "Opening logfile '%s'\n", optLogFilename); | |
721 | |
722 if ((optLogFile = fopen(optLogFilename, "a")) == NULL) { | |
723 THERR("Could not open logfile for appending!\n"); | |
724 return -9; | |
725 } | |
726 } | |
727 | |
10 | 728 #ifdef __WIN32 |
729 { | |
730 WSADATA wsaData; | |
731 if (WSAStartup(MAKEWORD(2, 0), &wsaData) != 0) { | |
732 THERR("WinSock API v2.0 not supported.\n"); | |
733 return -20; | |
734 } | |
735 } | |
736 #endif | |
737 | |
0 | 738 /* Okay ... */ |
739 THMSG(1, "Trying to resolve host '%s' ...\n", optServer); | |
740 tmpHost = gethostbyname(optServer); | |
741 if (tmpHost == NULL) { | |
742 THERR("Could not resolve hostname: %s.\n", | |
743 hstrerror(h_errno)); | |
744 return -3; | |
745 } | |
746 THMSG(2, "True hostname: %s\n", tmpHost->h_name); | |
747 | |
13
86fe5f0d1a85
Cleanups; Added probing connection (requesting some policy crap) to emulate the official client.
Matti Hamalainen <ccr@tnsp.org>
parents:
12
diff
changeset
|
748 /* To emulate the official client, we first make a fake connection ... */ |
86fe5f0d1a85
Cleanups; Added probing connection (requesting some policy crap) to emulate the official client.
Matti Hamalainen <ccr@tnsp.org>
parents:
12
diff
changeset
|
749 if ((tmpSocket = openConnection((struct in_addr *) tmpHost->h_addr, optPort)) < 0) { |
86fe5f0d1a85
Cleanups; Added probing connection (requesting some policy crap) to emulate the official client.
Matti Hamalainen <ccr@tnsp.org>
parents:
12
diff
changeset
|
750 THERR("Fakeprobe connection setup failed!\n"); |
86fe5f0d1a85
Cleanups; Added probing connection (requesting some policy crap) to emulate the official client.
Matti Hamalainen <ccr@tnsp.org>
parents:
12
diff
changeset
|
751 goto err_exit; |
0 | 752 } |
753 | |
13
86fe5f0d1a85
Cleanups; Added probing connection (requesting some policy crap) to emulate the official client.
Matti Hamalainen <ccr@tnsp.org>
parents:
12
diff
changeset
|
754 tmpStr = "<policy-file-request/>"; |
23
40fecbab1dc1
Check for error condition properly.
Matti Hamalainen <ccr@tnsp.org>
parents:
22
diff
changeset
|
755 if (sendToSocket(tmpSocket, tmpStr, strlen(tmpStr) + 1) == FALSE) { |
13
86fe5f0d1a85
Cleanups; Added probing connection (requesting some policy crap) to emulate the official client.
Matti Hamalainen <ccr@tnsp.org>
parents:
12
diff
changeset
|
756 THERR("Failed to send fakeprobe.\n"); |
86fe5f0d1a85
Cleanups; Added probing connection (requesting some policy crap) to emulate the official client.
Matti Hamalainen <ccr@tnsp.org>
parents:
12
diff
changeset
|
757 goto err_exit; |
86fe5f0d1a85
Cleanups; Added probing connection (requesting some policy crap) to emulate the official client.
Matti Hamalainen <ccr@tnsp.org>
parents:
12
diff
changeset
|
758 } else { |
86fe5f0d1a85
Cleanups; Added probing connection (requesting some policy crap) to emulate the official client.
Matti Hamalainen <ccr@tnsp.org>
parents:
12
diff
changeset
|
759 ssize_t gotBuf; |
86fe5f0d1a85
Cleanups; Added probing connection (requesting some policy crap) to emulate the official client.
Matti Hamalainen <ccr@tnsp.org>
parents:
12
diff
changeset
|
760 char tmpBuf[4096]; |
86fe5f0d1a85
Cleanups; Added probing connection (requesting some policy crap) to emulate the official client.
Matti Hamalainen <ccr@tnsp.org>
parents:
12
diff
changeset
|
761 gotBuf = recv(tmpSocket, tmpBuf, sizeof(tmpBuf), 0); |
86fe5f0d1a85
Cleanups; Added probing connection (requesting some policy crap) to emulate the official client.
Matti Hamalainen <ccr@tnsp.org>
parents:
12
diff
changeset
|
762 tmpBuf[gotBuf-1] = 0; |
86fe5f0d1a85
Cleanups; Added probing connection (requesting some policy crap) to emulate the official client.
Matti Hamalainen <ccr@tnsp.org>
parents:
12
diff
changeset
|
763 THMSG(2, "Probe got: %s\n", tmpBuf); |
86fe5f0d1a85
Cleanups; Added probing connection (requesting some policy crap) to emulate the official client.
Matti Hamalainen <ccr@tnsp.org>
parents:
12
diff
changeset
|
764 closeConnection(tmpSocket); |
0 | 765 } |
766 | |
13
86fe5f0d1a85
Cleanups; Added probing connection (requesting some policy crap) to emulate the official client.
Matti Hamalainen <ccr@tnsp.org>
parents:
12
diff
changeset
|
767 /* Okay, now do the proper connection ... */ |
86fe5f0d1a85
Cleanups; Added probing connection (requesting some policy crap) to emulate the official client.
Matti Hamalainen <ccr@tnsp.org>
parents:
12
diff
changeset
|
768 if ((tmpSocket = openConnection((struct in_addr *) tmpHost->h_addr, optPort)) < 0) { |
86fe5f0d1a85
Cleanups; Added probing connection (requesting some policy crap) to emulate the official client.
Matti Hamalainen <ccr@tnsp.org>
parents:
12
diff
changeset
|
769 THERR("Main connection setup failed!\n"); |
86fe5f0d1a85
Cleanups; Added probing connection (requesting some policy crap) to emulate the official client.
Matti Hamalainen <ccr@tnsp.org>
parents:
12
diff
changeset
|
770 goto err_exit; |
86fe5f0d1a85
Cleanups; Added probing connection (requesting some policy crap) to emulate the official client.
Matti Hamalainen <ccr@tnsp.org>
parents:
12
diff
changeset
|
771 } |
86fe5f0d1a85
Cleanups; Added probing connection (requesting some policy crap) to emulate the official client.
Matti Hamalainen <ccr@tnsp.org>
parents:
12
diff
changeset
|
772 |
0 | 773 THMSG(1, "Connected, logging in as '%s'.\n", optUserName); |
774 optUserName2 = encodeStr1(optUserName); | |
775 | |
776 sendUserMsg(tmpSocket, optUserName2, "%%2Flogin%%20%%2Dsite%%20NN%%20%%2Dpassword%%20%s", optPassword); | |
777 | |
778 FD_ZERO(&inputfds); | |
779 FD_SET(0, &inputfds); | |
780 FD_ZERO(&sockfds); | |
781 FD_SET(tmpSocket, &sockfds); | |
782 | |
783 while (!exitProg) { | |
784 ssize_t gotBuf; | |
785 int result; | |
786 char tmpBuf[4096]; | |
787 fd_set tmpfds; | |
788 | |
789 /* Check for incoming data from the server */ | |
790 tv.tv_sec = 0; | |
791 tv.tv_usec = SET_SELECT_USEC; | |
792 tmpfds = sockfds; | |
793 if ((result = select(tmpSocket+1, &tmpfds, NULL, NULL, &tv)) == -1) { | |
9 | 794 printMsg("Error occured in select(sockfds): %s\n", strerror(errno)); |
0 | 795 exitProg = TRUE; |
796 } else if (FD_ISSET(tmpSocket, &tmpfds)) { | |
797 gotBuf = recv(tmpSocket, tmpBuf, sizeof(tmpBuf), 0); | |
798 | |
799 if (gotBuf < 0) { | |
9 | 800 printMsg("Error in recv: %s\n", strerror(errno)); |
0 | 801 exitProg = TRUE; |
802 } else if (gotBuf == 0) { | |
9 | 803 printMsg("Server closed connection.\n"); |
0 | 804 exitProg = TRUE; |
805 } else { | |
806 /* Handle protocol data */ | |
807 tmpBuf[gotBuf] = 0; | |
808 result = handleProtocol(tmpSocket, tmpBuf, gotBuf); | |
809 | |
810 if (result > 0) { | |
811 /* Couldn't handle the message for some reason */ | |
9 | 812 printMsg("Could not handle: %s\n", tmpBuf); |
0 | 813 } else if (result < 0) { |
814 /* Fatal error, quit */ | |
9 | 815 printMsg("Fatal error with message: %s\n", tmpBuf); |
0 | 816 exitProg = TRUE; |
817 } | |
818 } | |
819 } | |
820 | |
821 /* Check for user input */ | |
21 | 822 if (!optDaemon) { |
0 | 823 tv.tv_sec = 0; |
824 tv.tv_usec = SET_SELECT_USEC; | |
825 tmpfds = inputfds; | |
826 if ((result = select(1, &tmpfds, NULL, NULL, &tv)) == -1) { | |
9 | 827 printMsg("Error occured in select(inputfds): %s\n", strerror(errno)); |
0 | 828 exitProg = TRUE; |
829 } else if (FD_ISSET(0, &tmpfds)) { | |
830 gotBuf = read(0, tmpBuf, sizeof(tmpBuf)); | |
831 | |
832 if (gotBuf < 0) { | |
9 | 833 printMsg("Error in reading stdio.\n"); |
0 | 834 exitProg = TRUE; |
835 } else { | |
836 /* Call the user input handler */ | |
15 | 837 result = handleUserInput(tmpSocket, tmpBuf, gotBuf); |
0 | 838 if (result < 0) { |
9 | 839 printMsg("Fatal error handling user input: %s\n", |
0 | 840 tmpBuf); |
841 exitProg = TRUE; | |
842 } | |
843 } | |
844 } | |
21 | 845 } /* !optDaemon */ |
0 | 846 |
13
86fe5f0d1a85
Cleanups; Added probing connection (requesting some policy crap) to emulate the official client.
Matti Hamalainen <ccr@tnsp.org>
parents:
12
diff
changeset
|
847 if (!colorSet) { |
86fe5f0d1a85
Cleanups; Added probing connection (requesting some policy crap) to emulate the official client.
Matti Hamalainen <ccr@tnsp.org>
parents:
12
diff
changeset
|
848 colorSet = TRUE; |
86fe5f0d1a85
Cleanups; Added probing connection (requesting some policy crap) to emulate the official client.
Matti Hamalainen <ccr@tnsp.org>
parents:
12
diff
changeset
|
849 sendUserMsg(tmpSocket, optUserName2, "%%2FSetFontColor%%20%%2Dcolor%%20%06X", optUserColor); |
86fe5f0d1a85
Cleanups; Added probing connection (requesting some policy crap) to emulate the official client.
Matti Hamalainen <ccr@tnsp.org>
parents:
12
diff
changeset
|
850 } |
86fe5f0d1a85
Cleanups; Added probing connection (requesting some policy crap) to emulate the official client.
Matti Hamalainen <ccr@tnsp.org>
parents:
12
diff
changeset
|
851 |
0 | 852 fflush(stdout); |
853 fflush(stderr); | |
854 } | |
855 | |
13
86fe5f0d1a85
Cleanups; Added probing connection (requesting some policy crap) to emulate the official client.
Matti Hamalainen <ccr@tnsp.org>
parents:
12
diff
changeset
|
856 /* Shotdiwn */ |
86fe5f0d1a85
Cleanups; Added probing connection (requesting some policy crap) to emulate the official client.
Matti Hamalainen <ccr@tnsp.org>
parents:
12
diff
changeset
|
857 err_exit: |
0 | 858 th_free(optUserName2); |
10 | 859 |
13
86fe5f0d1a85
Cleanups; Added probing connection (requesting some policy crap) to emulate the official client.
Matti Hamalainen <ccr@tnsp.org>
parents:
12
diff
changeset
|
860 closeConnection(tmpSocket); |
86fe5f0d1a85
Cleanups; Added probing connection (requesting some policy crap) to emulate the official client.
Matti Hamalainen <ccr@tnsp.org>
parents:
12
diff
changeset
|
861 |
10 | 862 #ifdef __WIN32 |
863 WSACleanup(); | |
864 #endif | |
13
86fe5f0d1a85
Cleanups; Added probing connection (requesting some policy crap) to emulate the official client.
Matti Hamalainen <ccr@tnsp.org>
parents:
12
diff
changeset
|
865 |
86fe5f0d1a85
Cleanups; Added probing connection (requesting some policy crap) to emulate the official client.
Matti Hamalainen <ccr@tnsp.org>
parents:
12
diff
changeset
|
866 THMSG(1, "Connection terminated.\n"); |
0 | 867 |
868 if (optLogFile) { | |
869 THMSG(1, "Closing logfile.\n"); | |
870 fclose(optLogFile); | |
871 } | |
872 | |
873 | |
874 return 0; | |
875 } |