summaryrefslogtreecommitdiff
path: root/vchat-client.c
diff options
context:
space:
mode:
authorerdgeist <>2003-02-12 17:48:37 +0000
committererdgeist <>2003-02-12 17:48:37 +0000
commitdea6bf757aa9a875eab35b2b650412e7605f1308 (patch)
tree14ed8374c3a3862529313088375693a7de70d3a7 /vchat-client.c
CVS moved to erdgeist.org
Diffstat (limited to 'vchat-client.c')
-rwxr-xr-xvchat-client.c536
1 files changed, 536 insertions, 0 deletions
diff --git a/vchat-client.c b/vchat-client.c
new file mode 100755
index 0000000..ae369ab
--- /dev/null
+++ b/vchat-client.c
@@ -0,0 +1,536 @@
1/*
2 * vchat-client - alpha version
3 * vchat-client.c - main() and utility functions
4 *
5 * Copyright (C) 2001 Andreas Kotes <count@flatline.de>
6 *
7 * This program is free software. It can be redistributed and/or modified,
8 * provided that this copyright notice is kept intact. This program is
9 * distributed in the hope that it will be useful, but without any warranty;
10 * without even the implied warranty of merchantability or fitness for a
11 * particular purpose. In no event shall the copyright holder be liable for
12 * any direct, indirect, incidental or special damages arising in any way out
13 * of the use of this software.
14 *
15 */
16
17/* general includes */
18#include <sys/types.h>
19#include <sys/time.h>
20#include <string.h>
21#include <unistd.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <errno.h>
25#include <fcntl.h>
26#include <signal.h>
27#include <readline/readline.h>
28#include <openssl/ssl.h>
29#include "vchat.h"
30
31/* version of this module */
32unsigned char *vchat_cl_version = "$Id$";
33
34/* externally used variables */
35/* we're logged in */
36unsigned int loggedin = 0;
37/* we run as long as this is true */
38unsigned int status = 1;
39/* error string to show after exit */
40unsigned char errstr[ERRSTRSIZE] = "\0";
41
42/* locally global variables */
43/* our list of filedescriptors */
44static fd_set masterfds;
45
46/* declaration of configuration array */
47#include "vchat-config.h"
48
49/* servers filedescriptor from vchat-protocol.c */
50extern int serverfd;
51
52void setnoption (unsigned char *, unsigned char *);
53
54static void parsecfg(unsigned char *line) {
55 int bytes;
56 unsigned char *param=line;
57 unsigned char *value=NULL;
58
59 /* handle quotes value is empty, so wecan use it */
60 value = strchr(line,'#');
61 if (value) { /* the line contains a cute little quote */
62 value[0]='\0'; /* ignore the rest of the line */
63 }
64
65 /* now split the line into two parts */
66 value = strchr(line,'=');
67 if (!value) return; /* exit if strchr fails */
68 value[0]='\0';
69 value++;
70
71 /* "trim" values */
72 while ((value[0] == ' ')||(value[0] == '\t'))
73 value++;
74 bytes = strlen(value);
75 while ((value[bytes-1] == ' ')||(value[bytes-1] == '\t')) {
76 value[bytes-1] = '\0';
77 bytes=strlen(value);
78 }
79 /* bytes should be strlen(value) */
80 if ( value[bytes-1] == '"' ) value[bytes-1] = '\0';
81 if ( value[0] == '"' ) value++;
82
83 /* "trim" param */
84 while ((param[0] == ' ')||(param[0] == '\t'))
85 param++;
86 bytes = strlen(param);
87 while ((param[bytes-1] == ' ')||(param[bytes-1] == '\t')) {
88 param[bytes-1] = '\0';
89 bytes=strlen(param);
90 }
91 /* bytes should be strlen(param) */
92 if ( param[bytes-1] == '\"' ) param[bytes-1] = '\0';
93 if ( param[0] == '\"' ) param++;
94
95 if ((!param)||(!value)) return; /* failsave */
96
97 //fprintf(stderr,"\"%s\" -> \"%s\"\n",param,value);
98 setnoption(param,value);
99}
100
101static void parseformats(unsigned char *line) {
102 int i;
103 unsigned char *tmp = NULL;
104
105 /* read a format line from file, syntax is
106 FS_XXX = "formatstring"
107 */
108
109 while( *line == ' ') line++;
110
111 if( *line != '#') /* allow to comment out the line */
112 for (i = 0; formatstrings[i].formatstr; i++)
113 if (!strncasecmp(formatstrings[i].idstring, line, strlen( formatstrings[i].idstring) ))
114 {
115 unsigned char *tail = line + strlen( formatstrings[i].idstring);
116 while( *tail==' ' || *tail=='\t') tail++; /* and skip whitespaces */
117
118 if( *tail++ == '=' )
119 {
120 while( *tail==' ' || *tail=='\t') tail++;
121 if( *(tail++)=='\"' )
122 {
123 int j, k = 0, stringends = 0, backslash=0;
124 for ( j = 0; tail[j] && !stringends; j++)
125 {
126 switch( tail[j] ) {
127 case '^':
128 if ( tail[j+1] != '^' )
129 tmpstr[k++] = 1;
130 break;
131 case '\\':
132 backslash=1-backslash;
133 tmpstr[k++] = '\\';
134 break;
135 case '\"':
136 if (backslash) k--; else stringends = 1;
137 default:
138 tmpstr[k++] = tail[j];
139 backslash = 0;
140 }
141 }
142
143 if ( stringends && ( (tmp = (unsigned char *)malloc( 1 + j )) != NULL ) )
144 {
145 memcpy( tmp, tmpstr, k);
146 tmp[k-1]=0;
147 formatstrings[i].formatstr = tmp;
148 }
149 }
150 }
151 }
152
153}
154
155/* UNUSED uncomment if needed
156static void parseknownhosts(unsigned char *line) {
157}
158*/
159
160/* load config file */
161static void
162loadcfg (unsigned char *file,void (*lineparser) (unsigned char *))
163{
164 int fd;
165 int bytes,bufoff=0;
166 unsigned char *tmp = NULL;
167#define BUFSIZE 4096
168 unsigned char buf[BUFSIZE]; /* data buffer */
169 unsigned char *ltmp = buf;
170 unsigned char *tildex = NULL;
171 buf[BUFSIZE-1] = '\0'; /* sanity stop */
172
173 if (!file) return;
174 if (!file[0]) return;
175 if (file[0] == '~')
176 tildex = tilde_expand(file);
177 else
178 tildex = file;
179 fd = open(tildex,O_RDONLY);
180 if (fd == -1) {
181 snprintf (errstr, TMPSTRSIZE, "Can't open config-file \"%s\": %s.", tildex, sys_errlist[errno]);
182 } else {
183 while ((bytes = read(fd,&buf[bufoff],BUFSIZE-bufoff-1))) {
184 if (bytes < 0) {
185 close(fd);
186 return;
187 } else {
188 /* terminate string */
189 buf[bytes + bufoff] = '\0';
190 /* as long as there are lines .. */
191 while ((tmp = strchr (ltmp, '\n')) != NULL) {
192 /* did the server send CR+LF instead of LF with the last line? */
193 if (tmp[-1] == '\r')
194 tmp[-1] = '\0';
195
196 /* remove newline from previous message, advance pointer of next
197 * message */
198 tmp[0] = '\0';
199 tmp++;
200
201 /* we have a last message? give away to line handler! */
202 if (ltmp[0])
203 {
204 lineparser(ltmp);
205 }
206
207 /* move line along .. */
208 ltmp = tmp;
209 }
210 /* buffer exhausted, move partial line to start of buffer and go
211 * on .. */
212 bufoff = (bytes+bufoff) - (ltmp-buf);
213 if (bufoff > 0)
214 memmove (buf, ltmp, bufoff);
215 else
216 bufoff = 0;
217 }
218 }
219 close(fd);
220 }
221}
222
223void
224loadconfig (unsigned char *file)
225{
226 loadcfg(file,parsecfg);
227}
228
229void
230loadformats (unsigned char *file)
231{
232 loadcfg(file,parseformats);
233}
234
235/* get-format-string */
236unsigned char *
237getformatstr (formtstr id)
238{
239 int i;
240 for (i = 0; formatstrings[i].formatstr; i++)
241 if (formatstrings[i].id == id) return formatstrings[i].formatstr;
242 return NULL;
243}
244
245/* get-string-option, fetches *char-value of variable named by option */
246unsigned char *
247getstroption (confopt option)
248{
249 int i;
250#ifdef DEBUG
251 fprintf(stderr,"getstroption: %d\n",option);
252#endif
253 for (i = 0; configoptions[i].type != CO_NIL; i++)
254 if ((configoptions[i].id == option) && (configoptions[i].type == CO_STR)) {
255 if (!configoptions[i].value)
256 return configoptions[i].defaultvalue;
257 else
258 return configoptions[i].value;
259 }
260 return NULL;
261}
262
263/* set-string-option, puts *char-value to variable named by option */
264void
265setstroption (confopt option, unsigned char *string)
266{
267 int i;
268#ifdef DEBUG
269 fprintf(stderr,"setstroption: %d to %s\n",option,string);
270#endif
271 for (i = 0; configoptions[i].type != CO_NIL; i++)
272 if ((configoptions[i].id == option) && (configoptions[i].type == CO_STR)) {
273 if (configoptions[i].value)
274 free(configoptions[i].value);
275 if (string)
276 configoptions[i].value = strdup(string);
277 else
278 configoptions[i].value = NULL;
279 if (configoptions[i].localvar)
280 *configoptions[i].localvar = configoptions[i].value;
281 }
282}
283
284/* set-named-option, puts string to variable named by name */
285void
286setnoption (unsigned char *name, unsigned char *string)
287{
288 int i;
289#ifdef DEBUG
290 fprintf(stderr,"setstrnoption: %s to %s\n",name,string);
291#endif
292 for (i = 0; configoptions[i].type != CO_NIL; i++)
293 if (!strcmp(configoptions[i].varname,name)) {
294 if (configoptions[i].type == CO_STR) {
295 if (configoptions[i].value)
296 free(configoptions[i].value);
297 if (string)
298 configoptions[i].value = strdup(string);
299 else
300 configoptions[i].value = NULL;
301 } else if (configoptions[i].type == CO_INT) {
302 configoptions[i].value = (char *) atoi(string);
303 }
304 if (configoptions[i].localvar)
305 *configoptions[i].localvar = configoptions[i].value;
306 }
307}
308
309/* get-integer-option, fetches int-value of variable named by option */
310int
311getintoption (confopt option)
312{
313 int i;
314#ifdef DEBUG
315 fprintf(stderr,"getintoption: %d\n",option);
316#endif
317 for (i = 0; configoptions[i].type != CO_NIL; i++)
318 if ((configoptions[i].id == option) && (configoptions[i].type == CO_INT)) {
319 if ((int)configoptions[i].value == -1)
320 return (int) configoptions[i].defaultvalue;
321 else
322 return (int) configoptions[i].value;
323 }
324 return 0;
325}
326
327/* set-integer-option, puts int-value to variable named by option */
328void
329setintoption (confopt option, int value)
330{
331 int i;
332#ifdef DEBUG
333 fprintf(stderr,"setintoption: %d to %d\n",option,value);
334#endif
335 for (i = 0; configoptions[i].type != CO_NIL; i++)
336 if ((configoptions[i].id == option) && (configoptions[i].type == CO_INT)) {
337 configoptions[i].value = (char *) value;
338 if (configoptions[i].localvar)
339 *configoptions[i].localvar = configoptions[i].value;
340 }
341}
342
343int quitrequest = 0;
344
345/* cleanup-hook, for SIGINT */
346void
347cleanup (int signal)
348{
349 if( signal == SIGINT ) {
350 switch( quitrequest >> 2 ) {
351 case 0:
352 flushout( );
353 writeout( " Press Ctrl+C twice now to confirm ");
354 showout( );
355 quitrequest+=4;
356 return;
357 break;
358 case 1:
359 flushout( );
360 writeout( " Press Ctrl+C twice now to confirm ");
361 writeout( " Press Ctrl+C once now to confirm ");
362 showout( );
363 quitrequest+=4;
364 return;
365 break;
366 default:
367 break;
368 }
369 }
370 /* restore terminal state */
371 exitui ();
372 /* clear userlist */
373 ul_clear ();
374 /* close server connection */
375 if (serverfd > 0)
376 close (serverfd);
377 /* inform user if we where killed by signal */
378 if (signal > 1)
379 {
380 fprintf (stderr, "vchat-client: terminated with signal %d.\n", signal);
381 } else if (errstr[0])
382 fprintf (stderr, errstr);
383 /* end of story */
384 exit (0);
385}
386
387void calleverysecond( void ) {
388 /* timetriggered execution, don't rely on being called every 1000us */
389 /* rather see it as a chance for being called 9 times in 10 seconds */
390 /* so check time() */
391
392 if(quitrequest)
393 quitrequest--;
394 if(outputcountdown && !--outputcountdown)
395 hideout( );
396}
397
398/* this function is called in the master loop */
399void
400eventloop (void)
401{
402 /* get fresh copy of filedescriptor list */
403 fd_set readfds = masterfds;
404 struct timeval tv = { 1, 0};
405
406 switch (select (serverfd + 1, &readfds, NULL, NULL, &tv))
407 {
408 case -1:
409 /* EINTR is most likely a SIGWINCH - ignore for now */
410 if (errno != EINTR)
411 {
412 snprintf (tmpstr, TMPSTRSIZE, "Select fails, %s.", sys_errlist[errno]);
413 strncpy(errstr,tmpstr,TMPSTRSIZE-2);
414 errstr[TMPSTRSIZE-2] = '\0';
415 strcat(errstr,"\n");
416 writecf (FS_ERR,tmpstr);
417 /* see this as an error condition and bail out */
418 status = 0;
419 }
420 break;
421 case 0:
422 /* time out reached */
423 calleverysecond();
424 break;
425 default:
426 /* something to read from user & we're logged in or have a cert? */
427 if (FD_ISSET (0, &readfds) && loggedin)
428 userinput ();
429
430 /* something to read from server? */
431 if (FD_ISSET (serverfd, &readfds))
432 networkinput ();
433 break;
434 }
435}
436
437void usage(unsigned char *name) {
438 printf("usage: %s [-C config-file] [-l] [-z] [-s host] [-p port] [-c channel] [-n nickname]\n",name);
439 printf(" -C load a second config-file, overriding the first one\n");
440 printf(" -l local connect (no SSL + connects localhost:2323)\n");
441 printf(" -z don't use certificate files\n");
442 printf(" -s set server (default \"%s\")\n",getstroption(CF_SERVERHOST));
443 printf(" -p set port (default %d)\n",getintoption(CF_SERVERPORT));
444 printf(" -c set channel (default %d)\n",getintoption(CF_CHANNEL));
445 if (nick)
446 printf(" -n set nickname (default \"%s\")\n",nick);
447 else
448 printf(" -n set nickname\n");
449 printf(" -f set from (default \"%s\")\n",getstroption(CF_FROM));
450 printf(" -h gives this help\n");
451}
452
453/* main - d'oh */
454int
455main (int argc, char **argv)
456{
457 int pchar;
458 int cmdsunparsed = 1;
459
460 loadconfig (GLOBAL_CONFIG_FILE);
461 loadconfig (getstroption (CF_CONFIGFILE));
462 loadformats(GLOBAL_FORMAT_FILE);
463 loadformats(getstroption (CF_FORMFILE));
464
465 /* parse commandline */
466 while (cmdsunparsed) {
467 pchar = getopt(argc,argv,"C:lzs:p:c:n:f:h");
468#ifdef DEBUG
469 fprintf(stderr,"parse commandline: %d ('%c'): %s\n",pchar,pchar,optarg);
470#endif
471
472 switch (pchar) {
473 case -1 : cmdsunparsed = 0; break;
474 case 'C': loadconfig(optarg); break;
475 case 'l': setintoption(CF_USESSL,0); break;
476 case 'z': setintoption(CF_USECERT,0); break;
477 case 's': setstroption(CF_SERVERHOST,optarg); break;
478 case 'p': setintoption(CF_SERVERPORT,strtol(optarg,NULL,10)); break;
479 case 'c': setintoption(CF_CHANNEL,strtol(optarg,NULL,10)); break;
480 case 'n': setstroption(CF_NICK,optarg); break;
481 case 'f': setstroption(CF_FROM,optarg); break;
482 case 'h': usage(argv[0]); exit(0); break;
483 default : usage(argv[0]); exit(1);
484 }
485 }
486
487 if (optind < argc) { usage(argv[0]); exit(1); }
488
489 if (!getintoption(CF_USESSL)) {
490 setstroption(CF_SERVERHOST,"localhost");
491 setintoption(CF_SERVERPORT,2323);
492 } else {
493 SSL_library_init ();
494 SSL_load_error_strings ();
495 }
496
497
498 /* install signal handler */
499 signal (SIGINT, cleanup);
500 signal (SIGHUP, cleanup);
501 signal (SIGTERM, cleanup);
502 signal (SIGQUIT, cleanup);
503
504 /* initialize userinterface */
505 initui ();
506
507 /* attempt connection */
508 if (!vcconnect (getstroption(CF_SERVERHOST), getintoption(CF_SERVERPORT)))
509 {
510 snprintf (tmpstr, TMPSTRSIZE, "Could not connect to server, %s.",
511 sys_errlist[errno]);
512 strncpy(errstr,tmpstr,TMPSTRSIZE-2);
513 errstr[TMPSTRSIZE-2] = '\0';
514 strcat(errstr,"\n");
515 writecf (FS_ERR,tmpstr);
516 /* exit condition */
517 status = 0;
518 }
519 else
520 {
521 /* add stdin & server to masterdfs */
522 FD_ZERO (&masterfds);
523 FD_SET (0, &masterfds);
524 FD_SET (serverfd, &masterfds);
525 }
526
527 while (status)
528 eventloop ();
529
530 /* sanely close connection to server */
531 vcdisconnect ();
532
533 /* call cleanup-hook without signal */
534 cleanup (0);
535 return 0;
536}