diff options
-rw-r--r--[-rwxr-xr-x] | Makefile | 42 | ||||
-rw-r--r--[-rwxr-xr-x] | README | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | TODO | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | debian/changelog | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | debian/control | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | debian/copyright | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | debian/dirs | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | debian/docs | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | debian/rules | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | filters | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | sample-count.fmt | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | sample-devlog.fmt | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | sample-erdgeist.fmt | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | sample-mAsq.fmt | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | sample-oldstyle.fmt | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | sample-xzivi.fmt | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | vchat-client.c | 624 | ||||
-rw-r--r--[-rwxr-xr-x] | vchat-client.sgml | 2 | ||||
-rw-r--r--[-rwxr-xr-x] | vchat-commands.c | 523 | ||||
-rw-r--r--[-rwxr-xr-x] | vchat-config.h | 5 | ||||
-rw-r--r-- | vchat-connection.c | 350 | ||||
-rw-r--r-- | vchat-connection.h | 11 | ||||
-rw-r--r--[-rwxr-xr-x] | vchat-help.h | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | vchat-howto | 0 | ||||
-rwxr-xr-x | vchat-keygen | 7 | ||||
-rw-r--r--[-rwxr-xr-x] | vchat-messages.h | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | vchat-protocol.c | 799 | ||||
-rwxr-xr-x | vchat-ssl.c | 475 | ||||
-rwxr-xr-x | vchat-ssl.h | 23 | ||||
-rw-r--r-- | vchat-tls.c | 970 | ||||
-rw-r--r-- | vchat-tls.h | 58 | ||||
-rw-r--r--[-rwxr-xr-x] | vchat-ui.c | 1984 | ||||
-rw-r--r--[-rwxr-xr-x] | vchat-user.c | 399 | ||||
-rw-r--r--[-rwxr-xr-x] | vchat.h | 236 | ||||
-rw-r--r--[-rwxr-xr-x] | vchatrc.ex | 0 |
35 files changed, 3702 insertions, 2806 deletions
@@ -6,18 +6,42 @@ | |||
6 | # configuration # | 6 | # configuration # |
7 | ############################################## | 7 | ############################################## |
8 | 8 | ||
9 | CFLAGS = -Wall -Os | 9 | OBJS = vchat-client.o vchat-ui.o vchat-protocol.o vchat-user.o vchat-commands.o vchat-tls.o vchat-connection.o |
10 | #CFLAGS = -Wall -g -ggdb | 10 | |
11 | # On FreeBSD you might want to link -ncursesw | ||
12 | LIBS = -lncurses | ||
13 | #LIBS = -lncursesw | ||
14 | |||
15 | LIBS += -lreadline | ||
16 | |||
17 | CFLAGS += -Wall -Os | ||
18 | CFLAGS += -I/usr/local/include | ||
19 | LDFLAGS += -L/usr/local/lib | ||
11 | 20 | ||
12 | ## use this line when you've got an readline before 4.(x|2) | 21 | ## use this line when you've got an readline before 4.(x|2) |
13 | #CFLAGS += -DOLDREADLINE | 22 | #CFLAGS += -DOLDREADLINE |
14 | 23 | ||
24 | # Alternatively, you can just build with make OLDREADLINE=-DOLDREADLINE | ||
25 | # if you can't modify this Makefile | ||
15 | CFLAGS += $(OLDREADLINE) | 26 | CFLAGS += $(OLDREADLINE) |
16 | 27 | ||
28 | ##### Enable this for enabling the OpenSSL library | ||
29 | CFLAGS += -DTLS_LIB_OPENSSL | ||
30 | LIBS += -lssl -lcrypto | ||
31 | |||
32 | ##### Enable this for enabling the mbedTLS library | ||
33 | #CFLAGS += -DTLS_LIB_MBEDTLS | ||
34 | #LIBS += -lmbedx509 -lmbedtls -lmbedcrypto | ||
35 | |||
17 | ## you might need one or more of these: | 36 | ## you might need one or more of these: |
37 | #CFLAGS+= -Wextra -Wall -g -ggdb | ||
38 | #CFLAGS+= -arch x86_64 -Wno-deprecated-declarations | ||
39 | #CFLAGS+= -arch i386 -Wno-deprecated-declarations | ||
18 | #CFLAGS += -I/usr/local/ssl/include -L/usr/local/ssl/lib | 40 | #CFLAGS += -I/usr/local/ssl/include -L/usr/local/ssl/lib |
19 | #CFLAGS += -I/usr/local/include -L/usr/local/lib | ||
20 | #CFLAGS += -I/usr/pkg/include -L/usr/pkg/lib | 41 | #CFLAGS += -I/usr/pkg/include -L/usr/pkg/lib |
42 | #LDFLAGS += -L"/usr/local/opt/openssl@1.1/lib" | ||
43 | #CFLAGS += -I../readline-6.3 | ||
44 | #LIBS += ../readline-6.3/libreadline.a | ||
21 | 45 | ||
22 | ## enable dietlibc | 46 | ## enable dietlibc |
23 | #CC = diet cc | 47 | #CC = diet cc |
@@ -29,9 +53,6 @@ CFLAGS += $(OLDREADLINE) | |||
29 | ## the install prefix best is /usr/local | 53 | ## the install prefix best is /usr/local |
30 | PREFIX=/usr/local | 54 | PREFIX=/usr/local |
31 | 55 | ||
32 | LIBS = -lreadline -lncursesw -lssl -lcrypto | ||
33 | OBJS = vchat-client.o vchat-ui.o vchat-protocol.o vchat-user.o vchat-commands.o vchat-ssl.o | ||
34 | |||
35 | 56 | ||
36 | ############################################## | 57 | ############################################## |
37 | # general targets # | 58 | # general targets # |
@@ -66,7 +87,7 @@ clean: | |||
66 | ############################################## | 87 | ############################################## |
67 | 88 | ||
68 | vchat-client: $(OBJS) | 89 | vchat-client: $(OBJS) |
69 | $(CC) $(CFLAGS) -o vchat-client $(OBJS) $(LIBS) | 90 | $(CC) $(CFLAGS) -o vchat-client $(OBJS) $(LIBS) $(LDFLAGS) |
70 | 91 | ||
71 | vchat-client.o: vchat-client.c vchat-config.h Makefile | 92 | vchat-client.o: vchat-client.c vchat-config.h Makefile |
72 | $(CC) $(CFLAGS) -o vchat-client.o -c vchat-client.c | 93 | $(CC) $(CFLAGS) -o vchat-client.o -c vchat-client.c |
@@ -83,8 +104,11 @@ vchat-user.o: vchat-user.c vchat.h | |||
83 | vchat-commands.o: vchat-commands.c vchat.h vchat-config.h | 104 | vchat-commands.o: vchat-commands.c vchat.h vchat-config.h |
84 | $(CC) $(CFLAGS) -o vchat-commands.o -c vchat-commands.c | 105 | $(CC) $(CFLAGS) -o vchat-commands.o -c vchat-commands.c |
85 | 106 | ||
86 | vchat-ssl.o: vchat-ssl.c vchat-ssl.h | 107 | vchat-tls.o: vchat-tls.c vchat-tls.h |
87 | $(CC) $(CFLAGS) -o vchat-ssl.o -c vchat-ssl.c | 108 | $(CC) $(CFLAGS) -o vchat-tls.o -c vchat-tls.c |
109 | |||
110 | vchat-connection.o: vchat-connection.c vchat-connection.h | ||
111 | $(CC) $(CFLAGS) -o vchat-connection.o -c vchat-connection.c | ||
88 | 112 | ||
89 | #vchat-client.1: vchat-client.sgml | 113 | #vchat-client.1: vchat-client.sgml |
90 | # docbook2man vchat-client.sgml | 114 | # docbook2man vchat-client.sgml |
diff --git a/debian/changelog b/debian/changelog index eb2b185..eb2b185 100755..100644 --- a/debian/changelog +++ b/debian/changelog | |||
diff --git a/debian/control b/debian/control index 3ba3fcd..3ba3fcd 100755..100644 --- a/debian/control +++ b/debian/control | |||
diff --git a/debian/copyright b/debian/copyright index e066d1d..e066d1d 100755..100644 --- a/debian/copyright +++ b/debian/copyright | |||
diff --git a/debian/dirs b/debian/dirs index e772481..e772481 100755..100644 --- a/debian/dirs +++ b/debian/dirs | |||
diff --git a/debian/docs b/debian/docs index 724e084..724e084 100755..100644 --- a/debian/docs +++ b/debian/docs | |||
diff --git a/debian/rules b/debian/rules index 778533f..778533f 100755..100644 --- a/debian/rules +++ b/debian/rules | |||
diff --git a/sample-count.fmt b/sample-count.fmt index bab9751..bab9751 100755..100644 --- a/sample-count.fmt +++ b/sample-count.fmt | |||
diff --git a/sample-devlog.fmt b/sample-devlog.fmt index ac30551..ac30551 100755..100644 --- a/sample-devlog.fmt +++ b/sample-devlog.fmt | |||
diff --git a/sample-erdgeist.fmt b/sample-erdgeist.fmt index 7545d3d..7545d3d 100755..100644 --- a/sample-erdgeist.fmt +++ b/sample-erdgeist.fmt | |||
diff --git a/sample-mAsq.fmt b/sample-mAsq.fmt index af001d7..af001d7 100755..100644 --- a/sample-mAsq.fmt +++ b/sample-mAsq.fmt | |||
diff --git a/sample-oldstyle.fmt b/sample-oldstyle.fmt index d0d8f17..d0d8f17 100755..100644 --- a/sample-oldstyle.fmt +++ b/sample-oldstyle.fmt | |||
diff --git a/sample-xzivi.fmt b/sample-xzivi.fmt index dbbf2b4..dbbf2b4 100755..100644 --- a/sample-xzivi.fmt +++ b/sample-xzivi.fmt | |||
diff --git a/vchat-client.c b/vchat-client.c index 0e480be..92d1905 100755..100644 --- a/vchat-client.c +++ b/vchat-client.c | |||
@@ -15,24 +15,27 @@ | |||
15 | */ | 15 | */ |
16 | 16 | ||
17 | /* general includes */ | 17 | /* general includes */ |
18 | #include <sys/types.h> | 18 | #include <errno.h> |
19 | #include <sys/time.h> | 19 | #include <locale.h> |
20 | #include <signal.h> | ||
20 | #include <stdint.h> | 21 | #include <stdint.h> |
21 | #include <time.h> | ||
22 | #include <string.h> | ||
23 | #include <unistd.h> | ||
24 | #include <stdio.h> | 22 | #include <stdio.h> |
25 | #include <stdlib.h> | 23 | #include <stdlib.h> |
26 | #include <errno.h> | 24 | #include <string.h> |
27 | #include <signal.h> | 25 | #include <sys/time.h> |
26 | #include <sys/types.h> | ||
27 | #include <time.h> | ||
28 | #include <unistd.h> | ||
29 | |||
28 | #include <readline/readline.h> | 30 | #include <readline/readline.h> |
29 | #include <locale.h> | ||
30 | 31 | ||
31 | #include "vchat.h" | 32 | #include "vchat-connection.h" |
32 | #include "vchat-user.h" | 33 | #include "vchat-user.h" |
34 | #include "vchat.h" | ||
33 | 35 | ||
34 | /* version of this module */ | 36 | /* version of this module */ |
35 | const char *vchat_cl_version = "vchat-client.c $Id$"; | 37 | const char *vchat_cl_version = |
38 | "vchat-client.c $Id$"; | ||
36 | 39 | ||
37 | /* externally used variables */ | 40 | /* externally used variables */ |
38 | /* we're logged in */ | 41 | /* we're logged in */ |
@@ -43,7 +46,6 @@ int status = 1; | |||
43 | int ownquit = 0; | 46 | int ownquit = 0; |
44 | /* we set this, we DONT want to quit */ | 47 | /* we set this, we DONT want to quit */ |
45 | int wantreconnect = 0; | 48 | int wantreconnect = 0; |
46 | unsigned int want_tcp_keepalive = 0; | ||
47 | 49 | ||
48 | static int reconnect_delay = 6; | 50 | static int reconnect_delay = 6; |
49 | static time_t reconnect_time = 0; | 51 | static time_t reconnect_time = 0; |
@@ -51,149 +53,152 @@ static time_t reconnect_time = 0; | |||
51 | /* error string to show after exit */ | 53 | /* error string to show after exit */ |
52 | char errstr[ERRSTRSIZE] = "\0"; | 54 | char errstr[ERRSTRSIZE] = "\0"; |
53 | 55 | ||
54 | /* locally global variables */ | ||
55 | /* our list of filedescriptors */ | ||
56 | static fd_set masterfds; | ||
57 | |||
58 | /* declaration of configuration array */ | 56 | /* declaration of configuration array */ |
59 | #include "vchat-config.h" | 57 | #include "vchat-config.h" |
60 | 58 | ||
61 | /* servers filedescriptor from vchat-protocol.c */ | 59 | void setnoption(const char *, char *); |
62 | extern int serverfd; | ||
63 | |||
64 | void setnoption (char *, char *); | ||
65 | 60 | ||
66 | static void parsecfg(char *line) { | 61 | static void parsecfg(char *line) { |
67 | int bytes; | 62 | int bytes; |
68 | char *param=line; | 63 | char *param = line; |
69 | char *value=NULL; | 64 | char *value = NULL; |
70 | 65 | ||
71 | /* handle quotes value is empty, so we can use it */ | 66 | /* handle quotes value is empty, so we can use it */ |
72 | value = strchr(line,'#'); | 67 | value = strchr(line, '#'); |
73 | if (value) { /* the line contains a cute little quote */ | 68 | if (value) { /* the line contains a cute little quote */ |
74 | value[0]='\0'; /* ignore the rest of the line */ | 69 | value[0] = '\0'; /* ignore the rest of the line */ |
75 | } | 70 | } |
76 | 71 | ||
77 | /* now split the line into two parts */ | 72 | /* now split the line into two parts */ |
78 | value = strchr(line,'='); | 73 | value = strchr(line, '='); |
79 | if (!value) return; /* exit if strchr fails */ | 74 | if (!value) |
80 | value[0]='\0'; | 75 | return; /* exit if strchr fails */ |
76 | value[0] = '\0'; | ||
81 | value++; | 77 | value++; |
82 | 78 | ||
83 | /* "trim" values */ | 79 | /* "trim" values */ |
84 | while ((value[0] == ' ')||(value[0] == '\t')) | 80 | while ((value[0] == ' ') || (value[0] == '\t')) |
85 | value++; | 81 | value++; |
86 | bytes = strlen(value); | 82 | bytes = strlen(value); |
87 | while ((value[bytes-1] == ' ')||(value[bytes-1] == '\t')) { | 83 | while ((value[bytes - 1] == ' ') || (value[bytes - 1] == '\t')) { |
88 | value[bytes-1] = '\0'; | 84 | value[bytes - 1] = '\0'; |
89 | bytes=strlen(value); | 85 | bytes = strlen(value); |
90 | } | 86 | } |
91 | /* bytes should be strlen(value) */ | 87 | /* bytes should be strlen(value) */ |
92 | if ( value[bytes-1] == '"' ) value[bytes-1] = '\0'; | 88 | if (value[bytes - 1] == '"') |
93 | if ( value[0] == '"' ) value++; | 89 | value[bytes - 1] = '\0'; |
90 | if (value[0] == '"') | ||
91 | value++; | ||
94 | 92 | ||
95 | /* "trim" param */ | 93 | /* "trim" param */ |
96 | while ((param[0] == ' ')||(param[0] == '\t')) | 94 | while ((param[0] == ' ') || (param[0] == '\t')) |
97 | param++; | 95 | param++; |
98 | bytes = strlen(param); | 96 | bytes = strlen(param); |
99 | while ((param[bytes-1] == ' ')||(param[bytes-1] == '\t')) { | 97 | while ((param[bytes - 1] == ' ') || (param[bytes - 1] == '\t')) { |
100 | param[bytes-1] = '\0'; | 98 | param[bytes - 1] = '\0'; |
101 | bytes=strlen(param); | 99 | bytes = strlen(param); |
102 | } | 100 | } |
103 | /* bytes should be strlen(param) */ | 101 | /* bytes should be strlen(param) */ |
104 | if ( param[bytes-1] == '\"' ) param[bytes-1] = '\0'; | 102 | if (param[bytes - 1] == '\"') |
105 | if ( param[0] == '\"' ) param++; | 103 | param[bytes - 1] = '\0'; |
104 | if (param[0] == '\"') | ||
105 | param++; | ||
106 | 106 | ||
107 | if ((!param)||(!value)) return; /* failsave */ | 107 | if ((!param) || (!value)) |
108 | return; /* failsave */ | ||
108 | 109 | ||
109 | //fprintf(stderr,"\"%s\" -> \"%s\"\n",param,value); | 110 | // fprintf(stderr,"\"%s\" -> \"%s\"\n",param,value); |
110 | setnoption(param,value); | 111 | setnoption(param, value); |
111 | } | 112 | } |
112 | 113 | ||
113 | static void parseformats(char *line) { | 114 | static void parseformats(char *line) { |
114 | int i; | 115 | int i; |
115 | char *tmp = NULL; | 116 | char *tmp = NULL; |
116 | 117 | ||
117 | /* read a format line from file, syntax is | 118 | /* read a format line from file, syntax is |
118 | FS_XXX = "formatstring" | 119 | FS_XXX = "formatstring" |
119 | */ | 120 | */ |
120 | 121 | ||
121 | while( *line == ' ') line++; | 122 | while (*line == ' ') |
122 | 123 | line++; | |
123 | if( strlen( line ) > TMPSTRSIZE ) return; | 124 | |
124 | 125 | if (strlen(line) > TMPSTRSIZE) | |
125 | if( *line != '#') /* allow to comment out the line */ | 126 | return; |
126 | for (i = 0; formatstrings[i].formatstr; i++) | 127 | |
127 | if (!strncasecmp(formatstrings[i].idstring, line, strlen( formatstrings[i].idstring) )) | 128 | if (*line != '#') /* allow to comment out the line */ |
128 | { | 129 | for (i = 0; formatstrings[i].formatstr; i++) |
129 | char *tail = line + strlen( formatstrings[i].idstring); | 130 | if (!strncasecmp(formatstrings[i].idstring, line, |
130 | while( *tail==' ' || *tail=='\t') tail++; /* and skip whitespaces */ | 131 | strlen(formatstrings[i].idstring))) { |
131 | 132 | char *tail = line + strlen(formatstrings[i].idstring); | |
132 | if( *tail++ == '=' ) | 133 | while (*tail == ' ' || *tail == '\t') |
133 | { | 134 | tail++; /* and skip whitespaces */ |
134 | while( *tail==' ' || *tail=='\t') tail++; | 135 | |
135 | if( *(tail++)=='\"' ) | 136 | if (*tail++ == '=') { |
136 | { | 137 | while (*tail == ' ' || *tail == '\t') |
137 | int j, k = 0, stringends = 0, backslash=0; | 138 | tail++; |
138 | for ( j = 0; tail[j] && !stringends; j++) | 139 | if (*(tail++) == '\"') { |
139 | { | 140 | int j, k = 0, stringends = 0, backslash = 0; |
140 | switch( tail[j] ) { | 141 | for (j = 0; tail[j] && !stringends; j++) { |
141 | case '^': | 142 | switch (tail[j]) { |
142 | if ( tail[j+1] != '^' ) | 143 | case '^': |
143 | tmpstr[k++] = 1; | 144 | if (tail[j + 1] != '^') |
144 | break; | 145 | tmpstr[k++] = 1; |
145 | case '\\': | 146 | break; |
146 | backslash=1-backslash; | 147 | case '\\': |
147 | tmpstr[k++] = '\\'; | 148 | backslash = 1 - backslash; |
148 | break; | 149 | tmpstr[k++] = '\\'; |
149 | case '\"': | 150 | break; |
150 | if (backslash) k--; else stringends = 1; | 151 | case '\"': |
151 | default: | 152 | if (backslash) |
152 | tmpstr[k++] = tail[j]; | 153 | k--; |
153 | backslash = 0; | 154 | else |
154 | } | 155 | stringends = 1; |
155 | } | 156 | default: |
156 | 157 | tmpstr[k++] = tail[j]; | |
157 | if ( stringends && ( (tmp = (char *)malloc( 1 + j )) != NULL ) ) | 158 | backslash = 0; |
158 | { | ||
159 | memcpy( tmp, tmpstr, k); | ||
160 | tmp[k-1]=0; | ||
161 | formatstrings[i].formatstr = tmp; | ||
162 | } | ||
163 | } | ||
164 | } | 159 | } |
165 | } | 160 | } |
166 | 161 | ||
162 | if (stringends && ((tmp = (char *)malloc(1 + j)) != NULL)) { | ||
163 | memcpy(tmp, tmpstr, k); | ||
164 | tmp[k - 1] = 0; | ||
165 | formatstrings[i].formatstr = tmp; | ||
166 | } | ||
167 | } | ||
168 | } | ||
169 | } | ||
167 | } | 170 | } |
168 | 171 | ||
169 | /* UNUSED uncomment if needed | 172 | /* UNUSED uncomment if needed |
170 | static void parseknownhosts(char *line) { | 173 | static void parseknownhosts(char *line) { |
171 | } | 174 | } |
172 | */ | 175 | */ |
173 | 176 | ||
174 | /* load config file */ | 177 | /* load config file */ |
175 | void | 178 | void loadcfg(char *file, int complain, void (*lineparser)(char *)) { |
176 | loadcfg (char *file,int complain, void (*lineparser) (char *)) | ||
177 | { | ||
178 | FILE *fh; | 179 | FILE *fh; |
179 | #define BUFSIZE 4096 | 180 | #define BUFSIZE 4096 |
180 | char buf[BUFSIZE]; /* data buffer */ | 181 | char buf[BUFSIZE]; /* data buffer */ |
181 | char *tildex = NULL, *t; | 182 | char *tildex = NULL, *t; |
182 | 183 | ||
183 | /* Check and expand filename then open file */ | 184 | /* Check and expand filename then open file */ |
184 | if (!file) return; | 185 | if (!file) |
185 | tildex = tilde_expand( file ); | 186 | return; |
186 | if (!tildex) return; | 187 | tildex = tilde_expand(file); |
187 | fh = fopen( tildex, "r" ); | 188 | if (!tildex) |
188 | free( tildex ); | 189 | return; |
190 | fh = fopen(tildex, "r"); | ||
191 | free(tildex); | ||
189 | 192 | ||
190 | if (!fh) { | 193 | if (!fh) { |
191 | if( complain ) snprintf (errstr, TMPSTRSIZE, "Can't open config-file \"%s\": %s.", file, strerror(errno)); | 194 | if (complain) |
195 | snprintf(errstr, TMPSTRSIZE, "Can't open config-file \"%s\": %s.", file, | ||
196 | strerror(errno)); | ||
192 | return; | 197 | return; |
193 | } | 198 | } |
194 | 199 | ||
195 | while ( fgets( buf, sizeof(buf), fh ) ) { | 200 | while (fgets(buf, sizeof(buf), fh)) { |
196 | if( ( t = strchr( buf, '\n' ) ) ) | 201 | if ((t = strchr(buf, '\n'))) |
197 | *t = 0; | 202 | *t = 0; |
198 | lineparser(buf); | 203 | lineparser(buf); |
199 | } | 204 | } |
@@ -201,77 +206,62 @@ loadcfg (char *file,int complain, void (*lineparser) (char *)) | |||
201 | fclose(fh); | 206 | fclose(fh); |
202 | } | 207 | } |
203 | 208 | ||
204 | void | 209 | void loadconfig(char *file) { loadcfg(file, 1, parsecfg); } |
205 | loadconfig (char *file) | ||
206 | { | ||
207 | loadcfg(file,1,parsecfg); | ||
208 | } | ||
209 | 210 | ||
210 | void | 211 | void loadformats(char *file) { loadcfg(file, 0, parseformats); } |
211 | loadformats (char *file) | ||
212 | { | ||
213 | loadcfg(file,0,parseformats); | ||
214 | } | ||
215 | 212 | ||
216 | /* get-format-string */ | 213 | /* get-format-string */ |
217 | char * | 214 | char *getformatstr(formtstr id) { |
218 | getformatstr (formtstr id) | ||
219 | { | ||
220 | int i; | 215 | int i; |
221 | for (i = 0; formatstrings[i].formatstr; i++) | 216 | for (i = 0; formatstrings[i].formatstr; i++) |
222 | if (formatstrings[i].id == id) return formatstrings[i].formatstr; | 217 | if (formatstrings[i].id == id) |
218 | return formatstrings[i].formatstr; | ||
223 | return NULL; | 219 | return NULL; |
224 | } | 220 | } |
225 | 221 | ||
226 | /* get-string-option, fetches *char-value of variable named by option */ | 222 | /* get-string-option, fetches *char-value of variable named by option */ |
227 | char * | 223 | char *getstroption(confopt option) { |
228 | getstroption (confopt option) | ||
229 | { | ||
230 | int i; | 224 | int i; |
231 | #ifdef DEBUG | 225 | #ifdef DEBUG |
232 | fprintf(stderr,"getstroption: %d\n",option); | 226 | fprintf(stderr, "getstroption: %d\n", option); |
233 | #endif | 227 | #endif |
234 | for (i = 0; configoptions[i].type != CO_NIL; i++) | 228 | for (i = 0; configoptions[i].type != CO_NIL; i++) |
235 | if ((configoptions[i].id == option) && (configoptions[i].type == CO_STR)) { | 229 | if ((configoptions[i].id == option) && (configoptions[i].type == CO_STR)) { |
236 | if (!configoptions[i].value) | 230 | if (!configoptions[i].value) |
237 | return configoptions[i].defaultvalue; | 231 | return configoptions[i].defaultvalue; |
238 | else | 232 | else |
239 | return configoptions[i].value; | 233 | return configoptions[i].value; |
240 | } | 234 | } |
241 | return NULL; | 235 | return NULL; |
242 | } | 236 | } |
243 | 237 | ||
244 | /* set-string-option, puts *char-value to variable named by option */ | 238 | /* set-string-option, puts *char-value to variable named by option */ |
245 | void | 239 | void setstroption(confopt option, char *string) { |
246 | setstroption (confopt option, char *string) | ||
247 | { | ||
248 | int i; | 240 | int i; |
249 | #ifdef DEBUG | 241 | #ifdef DEBUG |
250 | fprintf(stderr,"setstroption: %d to %s\n",option,string); | 242 | fprintf(stderr, "setstroption: %d to %s\n", option, string); |
251 | #endif | 243 | #endif |
252 | for (i = 0; configoptions[i].type != CO_NIL; i++) | 244 | for (i = 0; configoptions[i].type != CO_NIL; i++) |
253 | if ((configoptions[i].id == option) && (configoptions[i].type == CO_STR)) { | 245 | if ((configoptions[i].id == option) && (configoptions[i].type == CO_STR)) { |
254 | if (configoptions[i].value) | 246 | if (configoptions[i].value) |
255 | free(configoptions[i].value); | 247 | free(configoptions[i].value); |
256 | if (string) | 248 | if (string) |
257 | configoptions[i].value = strdup(string); | 249 | configoptions[i].value = strdup(string); |
258 | else | 250 | else |
259 | configoptions[i].value = NULL; | 251 | configoptions[i].value = NULL; |
260 | if (configoptions[i].localvar.pstr) | 252 | if (configoptions[i].localvar.pstr) |
261 | *configoptions[i].localvar.pstr = configoptions[i].value; | 253 | *configoptions[i].localvar.pstr = configoptions[i].value; |
262 | } | 254 | } |
263 | } | 255 | } |
264 | 256 | ||
265 | /* set-named-option, puts string to variable named by name */ | 257 | /* set-named-option, puts string to variable named by name */ |
266 | void | 258 | void setnoption(const char *name, char *string) { |
267 | setnoption (char *name, char *string) | ||
268 | { | ||
269 | int i; | 259 | int i; |
270 | #ifdef DEBUG | 260 | #ifdef DEBUG |
271 | fprintf(stderr,"setstrnoption: %s to %s\n",name,string); | 261 | fprintf(stderr, "setstrnoption: %s to %s\n", name, string); |
272 | #endif | 262 | #endif |
273 | for (i = 0; configoptions[i].type != CO_NIL; i++) | 263 | for (i = 0; configoptions[i].type != CO_NIL; i++) |
274 | if (!strcmp(configoptions[i].varname,name)) { | 264 | if (!strcmp(configoptions[i].varname, name)) { |
275 | if (configoptions[i].type == CO_STR) { | 265 | if (configoptions[i].type == CO_STR) { |
276 | if (configoptions[i].value) | 266 | if (configoptions[i].value) |
277 | free(configoptions[i].value); | 267 | free(configoptions[i].value); |
@@ -280,276 +270,290 @@ setnoption (char *name, char *string) | |||
280 | else | 270 | else |
281 | configoptions[i].value = NULL; | 271 | configoptions[i].value = NULL; |
282 | if (configoptions[i].localvar.pstr) | 272 | if (configoptions[i].localvar.pstr) |
283 | *configoptions[i].localvar.pstr = configoptions[i].value; | 273 | *configoptions[i].localvar.pstr = configoptions[i].value; |
284 | } else if (configoptions[i].type == CO_INT) { | 274 | } else if (configoptions[i].type == CO_INT) { |
285 | configoptions[i].value = (char *)(uintptr_t)atoi(string); | 275 | configoptions[i].value = (char *)(uintptr_t)atoi(string); |
286 | if (configoptions[i].localvar.pint) | 276 | if (configoptions[i].localvar.pint) |
287 | *configoptions[i].localvar.pint = (uintptr_t)configoptions[i].value; | 277 | *configoptions[i].localvar.pint = (uintptr_t)configoptions[i].value; |
288 | } | 278 | } |
289 | } | 279 | } |
290 | } | 280 | } |
291 | 281 | ||
292 | /* get-integer-option, fetches int-value of variable named by option */ | 282 | /* get-integer-option, fetches int-value of variable named by option */ |
293 | int | 283 | int getintoption(confopt option) { |
294 | getintoption (confopt option) | ||
295 | { | ||
296 | int i; | 284 | int i; |
297 | #ifdef DEBUG | 285 | #ifdef DEBUG |
298 | fprintf(stderr,"getintoption: %d\n",option); | 286 | fprintf(stderr, "getintoption: %d\n", option); |
299 | #endif | 287 | #endif |
300 | for (i = 0; configoptions[i].type != CO_NIL; i++) | 288 | for (i = 0; configoptions[i].type != CO_NIL; i++) |
301 | if ((configoptions[i].id == option) && (configoptions[i].type == CO_INT)) { | 289 | if ((configoptions[i].id == option) && (configoptions[i].type == CO_INT)) { |
302 | if ((uintptr_t)configoptions[i].value == -1) | 290 | if ((intptr_t)configoptions[i].value == -1) |
303 | return (uintptr_t) configoptions[i].defaultvalue; | 291 | return (intptr_t)configoptions[i].defaultvalue; |
304 | else | 292 | else |
305 | return (uintptr_t) configoptions[i].value; | 293 | return (intptr_t)configoptions[i].value; |
306 | } | 294 | } |
307 | return 0; | 295 | return 0; |
308 | } | 296 | } |
309 | 297 | ||
310 | /* set-integer-option, puts int-value to variable named by option */ | 298 | /* set-integer-option, puts int-value to variable named by option */ |
311 | void | 299 | void setintoption(confopt option, int value) { |
312 | setintoption (confopt option, int value) | ||
313 | { | ||
314 | int i; | 300 | int i; |
315 | #ifdef DEBUG | 301 | #ifdef DEBUG |
316 | fprintf(stderr,"setintoption: %d to %d\n",option,value); | 302 | fprintf(stderr, "setintoption: %d to %d\n", option, value); |
317 | #endif | 303 | #endif |
318 | for (i = 0; configoptions[i].type != CO_NIL; i++) | 304 | for (i = 0; configoptions[i].type != CO_NIL; i++) |
319 | if ((configoptions[i].id == option) && (configoptions[i].type == CO_INT)) { | 305 | if ((configoptions[i].id == option) && (configoptions[i].type == CO_INT)) { |
320 | configoptions[i].value = (char *)(uintptr_t)value; | 306 | configoptions[i].value = (char *)(uintptr_t)value; |
321 | if (configoptions[i].localvar.pint) | 307 | if (configoptions[i].localvar.pint) |
322 | *configoptions[i].localvar.pint = (uintptr_t)configoptions[i].value; | 308 | *configoptions[i].localvar.pint = (uintptr_t)configoptions[i].value; |
323 | } | 309 | } |
324 | } | 310 | } |
325 | 311 | ||
326 | int quitrequest = 0; | 312 | int quitrequest = 0; |
327 | 313 | ||
328 | /* cleanup-hook, for SIGINT */ | 314 | /* cleanup-hook, for SIGINT */ |
329 | void | 315 | void cleanup(int signal) { |
330 | cleanup (int signal) | 316 | if (signal == SIGINT) { |
331 | { | 317 | switch (quitrequest >> 2) { |
332 | if( signal == SIGINT ) { | 318 | case 0: |
333 | switch( quitrequest >> 2 ) { | 319 | flushout(); |
334 | case 0: | 320 | writeout(" Press Ctrl+C twice now to confirm "); |
335 | flushout( ); | 321 | showout(); |
336 | writeout( " Press Ctrl+C twice now to confirm "); | 322 | quitrequest += 4; |
337 | showout( ); | 323 | return; |
338 | quitrequest+=4; | 324 | break; |
339 | return; | 325 | case 1: |
340 | break; | 326 | flushout(); |
341 | case 1: | 327 | writeout(" Press Ctrl+C twice now to confirm "); |
342 | flushout( ); | 328 | writeout(" Press Ctrl+C once now to confirm "); |
343 | writeout( " Press Ctrl+C twice now to confirm "); | 329 | showout(); |
344 | writeout( " Press Ctrl+C once now to confirm "); | 330 | quitrequest += 4; |
345 | showout( ); | 331 | return; |
346 | quitrequest+=4; | 332 | break; |
347 | return; | 333 | default: |
348 | break; | 334 | break; |
349 | default: | 335 | } |
350 | break; | ||
351 | } | ||
352 | } | 336 | } |
353 | /* restore terminal state */ | 337 | /* restore terminal state */ |
354 | exitui (); | 338 | exitui(); |
355 | /* clear userlist */ | 339 | /* clear userlist */ |
356 | ul_clear (); | 340 | ul_clear(); |
357 | /* close server connection */ | 341 | vc_disconnect(); |
358 | if (serverfd > 0) { | 342 | |
359 | close (serverfd); | ||
360 | serverfd = -1; | ||
361 | } | ||
362 | /* inform user if we where killed by signal */ | 343 | /* inform user if we where killed by signal */ |
363 | if (signal > 1) | 344 | if (signal > 1) { |
364 | { | 345 | fprintf(stderr, "vchat-client: terminated with signal %d.\n", signal); |
365 | fprintf (stderr, "vchat-client: terminated with signal %d.\n", signal); | 346 | if (!loggedin) |
366 | } else if (errstr[0]) | 347 | dumpconnect(); |
367 | fputs (errstr, stderr); | 348 | } else if (errstr[0]) { |
349 | fputs(errstr, stderr); | ||
350 | if (!loggedin) | ||
351 | dumpconnect(); | ||
352 | } | ||
368 | /* end of story */ | 353 | /* end of story */ |
369 | exit (0); | 354 | exit(0); |
370 | } | 355 | } |
371 | 356 | ||
372 | static int oldseconds = 0; | 357 | static int oldseconds = 0; |
373 | 358 | ||
374 | void calleverysecond( void ) { | 359 | void calleverysecond(void) { |
375 | /* timetriggered execution, don't rely on being called every 1000us */ | 360 | /* timetriggered execution, don't rely on being called every 1000us */ |
376 | /* rather see it as a chance for being called 9 times in 10 seconds */ | 361 | /* rather see it as a chance for being called 9 times in 10 seconds */ |
377 | /* so check time() */ | 362 | /* so check time() */ |
378 | time_t now = time( NULL ); | 363 | time_t now = time(NULL); |
379 | struct tm *mytime = localtime( &now ); | 364 | struct tm *mytime = localtime(&now); |
380 | if( mytime->tm_sec < oldseconds ) { | 365 | if (mytime->tm_sec < oldseconds) { |
381 | consoleline( NULL ); | 366 | consoleline(NULL); |
382 | } | 367 | } |
383 | oldseconds = mytime->tm_sec; | 368 | oldseconds = mytime->tm_sec; |
384 | 369 | ||
385 | if(quitrequest) | 370 | if (quitrequest) |
386 | quitrequest--; | 371 | quitrequest--; |
387 | if(outputcountdown && !--outputcountdown) | 372 | if (outputcountdown && !--outputcountdown) |
388 | hideout( ); | 373 | hideout(); |
389 | if( reconnect_time && ( time( NULL ) > reconnect_time ) ) | 374 | if (reconnect_time && (time(NULL) > reconnect_time)) |
390 | status = 0; | 375 | status = 0; |
391 | } | 376 | } |
392 | 377 | ||
393 | /* this function is called in the master loop */ | 378 | /* this function is called in the master loop */ |
394 | void | 379 | void eventloop(void) { |
395 | eventloop (void) | 380 | int poll_result = vc_poll(1 /* second timeout */); |
396 | { | 381 | |
397 | /* get fresh copy of filedescriptor list */ | 382 | switch (poll_result) { |
398 | fd_set readfds = masterfds; | ||
399 | struct timeval tv = { 1, 0}; | ||
400 | |||
401 | switch (select (serverfd + 2, &readfds, NULL, NULL, &tv)) | ||
402 | { | ||
403 | case -1: | 383 | case -1: |
404 | /* EINTR is most likely a SIGWINCH - ignore for now */ | 384 | /* EINTR is most likely a SIGWINCH - ignore for now */ |
405 | if (errno != EINTR) | 385 | if (errno != EINTR) { |
406 | { | 386 | snprintf(tmpstr, TMPSTRSIZE, "Select fails, %s.", strerror(errno)); |
407 | snprintf (tmpstr, TMPSTRSIZE, "Select fails, %s.", strerror(errno)); | 387 | strncpy(errstr, tmpstr, TMPSTRSIZE - 2); |
408 | strncpy(errstr,tmpstr,TMPSTRSIZE-2); | 388 | errstr[TMPSTRSIZE - 2] = '\0'; |
409 | errstr[TMPSTRSIZE-2] = '\0'; | 389 | strcat(errstr, "\n"); |
410 | strcat(errstr,"\n"); | 390 | writecf(FS_ERR, tmpstr); |
411 | writecf (FS_ERR,tmpstr); | 391 | /* see this as an error condition and bail out */ |
412 | /* see this as an error condition and bail out */ | 392 | status = 0; |
413 | status = 0; | 393 | } |
414 | } | 394 | break; |
415 | break; | ||
416 | case 0: | 395 | case 0: |
417 | /* time out reached */ | 396 | /* time out reached */ |
418 | calleverysecond(); | 397 | calleverysecond(); |
419 | break; | 398 | break; |
420 | default: | 399 | default: |
421 | /* something to read from user & we're logged in or have a cert? */ | 400 | /* something to read from user & we're logged in or have a cert? */ |
422 | if (FD_ISSET (0, &readfds) ) | 401 | if (poll_result & 1) |
423 | userinput (); | 402 | userinput(); |
424 | 403 | ||
425 | /* something to read from server? */ | 404 | /* something to read from server? */ |
426 | if (serverfd!=-1 && FD_ISSET (serverfd, &readfds)) | 405 | if ((poll_result & 2) && vc_receive()) |
427 | networkinput (); | 406 | status = 0; |
428 | break; | 407 | break; |
429 | } | 408 | } |
430 | } | 409 | } |
431 | 410 | ||
432 | void usage( char *name) { | 411 | void usage(char *name) { |
433 | printf ("usage: %s [-C config-file] [-F formats] [-l] [-z] [-s host] [-p port] [-c channel] [-n nickname]\n",name); | 412 | printf("usage: %s [-C config-file] [-F formats] [-l] [-z] [-s host] [-p " |
434 | puts (" -C load a second config-file, overriding the first one"); | 413 | "port] [-c channel] [-n nickname]\n", |
435 | puts (" -F load format strings (skins) from this file"); | 414 | name); |
436 | puts (" -l local connect (no SSL)"); | 415 | puts(" -C load a second config-file, overriding the first one"); |
437 | puts (" -z don't use certificate files"); | 416 | puts(" -F load format strings (skins) from this file"); |
438 | printf (" -s set server (default \"%s\")\n",getstroption(CF_SERVERHOST)); | 417 | puts(" -l local connect (no SSL)"); |
439 | printf (" -p set port (default %s)\n",getstroption(CF_SERVERPORT)); | 418 | puts(" -z don't use certificate files"); |
440 | printf (" -c set channel (default %d)\n",getintoption(CF_CHANNEL)); | 419 | printf(" -s set server (default \"%s\")\n", getstroption(CF_SERVERHOST)); |
441 | if (own_nick_get()) | 420 | printf(" -p set port (default %s)\n", getstroption(CF_SERVERPORT)); |
442 | printf(" -n set nickname (default \"%s\")\n",own_nick_get()); | 421 | printf(" -c set channel (default %d)\n", getintoption(CF_CHANNEL)); |
443 | else | 422 | if (own_nick_get()) |
444 | puts (" -n set nickname"); | 423 | printf(" -n set nickname (default \"%s\")\n", own_nick_get()); |
445 | printf (" -f set from (default \"%s\")\n",getstroption(CF_FROM)); | 424 | else |
446 | puts (" -h gives this help"); | 425 | puts(" -n set nickname"); |
447 | puts (" -v show module versions"); | 426 | printf(" -f set from (default \"%s\")\n", getstroption(CF_FROM)); |
427 | puts(" -h gives this help"); | ||
428 | puts(" -v show module versions"); | ||
448 | } | 429 | } |
449 | 430 | ||
450 | void versions() { | 431 | void versions() { |
451 | puts (vchat_cl_version); | 432 | puts(vchat_cl_version); |
452 | puts (vchat_ui_version); | 433 | puts(vchat_ui_version); |
453 | puts (vchat_io_version); | 434 | puts(vchat_io_version); |
454 | puts (vchat_us_version); | 435 | puts(vchat_us_version); |
455 | puts (vchat_cm_version); | 436 | puts(vchat_cm_version); |
456 | puts (vchat_ssl_version); | 437 | puts(vchat_tls_version); |
457 | puts (vchat_ssl_version_external); | 438 | puts(vchat_tls_version_external()); |
458 | } | 439 | } |
459 | 440 | ||
460 | /* main - d'oh */ | 441 | /* main - d'oh */ |
461 | int | 442 | int main(int argc, char **argv) { |
462 | main (int argc, char **argv) | ||
463 | { | ||
464 | int pchar; | 443 | int pchar; |
465 | int cmdsunparsed = 1; | 444 | int cmdsunparsed = 1; |
466 | 445 | ||
467 | setlocale(LC_ALL,""); | 446 | setlocale(LC_ALL, ""); |
468 | |||
469 | loadconfig (GLOBAL_CONFIG_FILE); | ||
470 | loadconfig (getstroption (CF_CONFIGFILE)); | ||
471 | 447 | ||
472 | /* make SSL version used visible */ | 448 | loadconfig(GLOBAL_CONFIG_FILE); |
473 | vchat_ssl_get_version_external(); | 449 | loadconfig(getstroption(CF_CONFIGFILE)); |
474 | 450 | ||
475 | /* parse commandline */ | 451 | /* parse commandline */ |
476 | while (cmdsunparsed) { | 452 | while (cmdsunparsed) { |
477 | pchar = getopt(argc,argv,"C:F:lzs:p:c:n:f:kKL:hv"); | 453 | pchar = getopt(argc, argv, "C:F:lzs:p:c:n:f:kKL:hv"); |
478 | #ifdef DEBUG | 454 | #ifdef DEBUG |
479 | fprintf(stderr,"parse commandline: %d ('%c'): %s\n",pchar,pchar,optarg); | 455 | fprintf(stderr, "parse commandline: %d ('%c'): %s\n", pchar, pchar, optarg); |
480 | #endif | 456 | #endif |
481 | 457 | ||
482 | switch (pchar) { | 458 | switch (pchar) { |
483 | case -1 : cmdsunparsed = 0; break; | 459 | case -1: |
484 | case 'C': loadconfig(optarg); break; | 460 | cmdsunparsed = 0; |
485 | case 'F': setstroption(CF_FORMFILE,optarg); break; | 461 | break; |
486 | case 'l': setintoption(CF_USESSL,0); break; | 462 | case 'C': |
487 | case 'z': setintoption(CF_USECERT,0); break; | 463 | loadconfig(optarg); |
488 | case 's': setstroption(CF_SERVERHOST,optarg); break; | 464 | break; |
489 | case 'p': setstroption(CF_SERVERPORT,optarg); break; | 465 | case 'F': |
490 | case 'c': setintoption(CF_CHANNEL,strtol(optarg,NULL,10)); break; | 466 | setstroption(CF_FORMFILE, optarg); |
491 | case 'n': own_nick_set(optarg); break; | 467 | break; |
492 | case 'f': setstroption(CF_FROM,optarg); break; | 468 | case 'l': |
493 | case 'h': usage(argv[0]); exit(0); break; | 469 | setintoption(CF_USESSL, 0); |
494 | case 'v': versions(); exit(0); break; | 470 | break; |
495 | default : usage(argv[0]); exit(1); | 471 | case 'z': |
496 | } | 472 | setintoption(CF_USECERT, 0); |
473 | break; | ||
474 | case 's': | ||
475 | setstroption(CF_SERVERHOST, optarg); | ||
476 | break; | ||
477 | case 'p': | ||
478 | setstroption(CF_SERVERPORT, optarg); | ||
479 | break; | ||
480 | case 'c': | ||
481 | setintoption(CF_CHANNEL, strtol(optarg, NULL, 10)); | ||
482 | break; | ||
483 | case 'n': | ||
484 | own_nick_set(optarg); | ||
485 | break; | ||
486 | case 'f': | ||
487 | setstroption(CF_FROM, optarg); | ||
488 | break; | ||
489 | case 'h': | ||
490 | usage(argv[0]); | ||
491 | exit(0); | ||
492 | break; | ||
493 | case 'v': | ||
494 | versions(); | ||
495 | exit(0); | ||
496 | break; | ||
497 | default: | ||
498 | usage(argv[0]); | ||
499 | exit(1); | ||
500 | } | ||
497 | } | 501 | } |
498 | 502 | ||
499 | if (optind < argc) { usage(argv[0]); exit(1); } | 503 | if (optind < argc) { |
504 | usage(argv[0]); | ||
505 | exit(1); | ||
506 | } | ||
500 | 507 | ||
501 | loadformats(GLOBAL_FORMAT_FILE); | 508 | loadformats(GLOBAL_FORMAT_FILE); |
502 | loadformats(getstroption (CF_FORMFILE)); | 509 | loadformats(getstroption(CF_FORMFILE)); |
503 | 510 | ||
504 | /* install signal handler */ | 511 | /* install signal handler */ |
505 | signal (SIGINT, cleanup); | 512 | signal(SIGINT, cleanup); |
506 | signal (SIGHUP, cleanup); | 513 | signal(SIGHUP, cleanup); |
507 | signal (SIGTERM, cleanup); | 514 | signal(SIGTERM, cleanup); |
508 | signal (SIGQUIT, cleanup); | 515 | signal(SIGQUIT, SIG_IGN); |
509 | 516 | ||
510 | /* initialize userinterface */ | 517 | /* initialize userinterface */ |
511 | initui (); | 518 | initui(); |
512 | |||
513 | while( status ) { | ||
514 | /* add stdin to masterfds */ | ||
515 | FD_ZERO (&masterfds); | ||
516 | FD_SET (0, &masterfds); | ||
517 | 519 | ||
520 | while (status) { | ||
518 | /* attempt connection */ | 521 | /* attempt connection */ |
519 | if (vcconnect (getstroption(CF_SERVERHOST), getstroption(CF_SERVERPORT))) { | 522 | if (vc_connect(getstroption(CF_SERVERHOST), getstroption(CF_SERVERPORT))) { |
520 | snprintf (tmpstr, TMPSTRSIZE, "Could not connect to server, %s.", strerror(errno)); | 523 | snprintf(tmpstr, TMPSTRSIZE, "Could not connect to server, %s.", |
521 | strncpy(errstr,tmpstr,TMPSTRSIZE-2); | 524 | strerror(errno)); |
522 | errstr[TMPSTRSIZE-2] = '\0'; | 525 | strncpy(errstr, tmpstr, TMPSTRSIZE - 2); |
523 | strcat(errstr,"\n"); | 526 | errstr[TMPSTRSIZE - 2] = '\0'; |
524 | writecf (FS_ERR,tmpstr); | 527 | strcat(errstr, "\n"); |
525 | 528 | writecf(FS_ERR, tmpstr); | |
526 | if( getintoption( CF_AUTORECONN ) ) { | 529 | |
527 | snprintf (tmpstr, TMPSTRSIZE, "reconnecting in %d seconds", reconnect_delay ); | 530 | if (getintoption(CF_AUTORECONN)) { |
528 | writecf (FS_ERR, tmpstr); | 531 | snprintf(tmpstr, TMPSTRSIZE, "reconnecting in %d seconds", |
529 | reconnect_delay = ( reconnect_delay * 15 ) / 10; | 532 | reconnect_delay); |
530 | reconnect_time = time( NULL ) + reconnect_delay; | 533 | writecf(FS_ERR, tmpstr); |
534 | reconnect_delay = (reconnect_delay * 15) / 10; | ||
535 | reconnect_time = time(NULL) + reconnect_delay; | ||
531 | } else | 536 | } else |
532 | status = 0; | 537 | status = 0; |
533 | } else { | 538 | } else { |
534 | /* add serverfd to masterfds, reset reconnect delay */ | 539 | /* reset reconnect delay */ |
535 | FD_SET (serverfd, &masterfds); | ||
536 | reconnect_delay = 6; | 540 | reconnect_delay = 6; |
537 | reconnect_time = 0; | 541 | reconnect_time = 0; |
538 | } | 542 | } |
539 | 543 | ||
540 | while (status) | 544 | while (status) |
541 | eventloop (); | 545 | eventloop(); |
542 | 546 | ||
543 | /* sanely close connection to server */ | 547 | /* sanely close connection to server */ |
544 | vcdisconnect (); | 548 | vc_disconnect(); |
545 | 549 | ||
546 | if( !ownquit && ( getintoption( CF_AUTORECONN ) || wantreconnect ) ) | 550 | if (!ownquit && (getintoption(CF_AUTORECONN) || wantreconnect)) |
547 | status = 1; | 551 | status = 1; |
548 | 552 | ||
549 | wantreconnect = 0; | 553 | wantreconnect = 0; |
550 | } | 554 | } |
551 | 555 | ||
552 | /* call cleanup-hook without signal */ | 556 | /* call cleanup-hook without signal */ |
553 | cleanup (0); | 557 | cleanup(0); |
554 | return 0; | 558 | return 0; |
555 | } | 559 | } |
diff --git a/vchat-client.sgml b/vchat-client.sgml index 3d6fbc7..7e96b28 100755..100644 --- a/vchat-client.sgml +++ b/vchat-client.sgml | |||
@@ -101,7 +101,7 @@ overridden in the configfile.</para></listitem> | |||
101 | 101 | ||
102 | <varlistentry> | 102 | <varlistentry> |
103 | <term><option>-f</option> <replaceable>from</replaceable></term> | 103 | <term><option>-f</option> <replaceable>from</replaceable></term> |
104 | <listitem><para>set from (default "vc-alpha-0.19")</para></listitem> | 104 | <listitem><para>set from (default "vc-alpha-0.20")</para></listitem> |
105 | </varlistentry> | 105 | </varlistentry> |
106 | 106 | ||
107 | <varlistentry> | 107 | <varlistentry> |
diff --git a/vchat-commands.c b/vchat-commands.c index 06c9010..3ef3132 100755..100644 --- a/vchat-commands.c +++ b/vchat-commands.c | |||
@@ -10,26 +10,29 @@ | |||
10 | * without even the implied warranty of merchantability or fitness for a | 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 | 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 | 12 | * any direct, indirect, incidental or special damages arising in any way out |
13 | * of the use of this software. | 13 | * of the use of this software. |
14 | * | 14 | * |
15 | */ | 15 | */ |
16 | 16 | ||
17 | /* general includes */ | 17 | /* general includes */ |
18 | #include <stdlib.h> | ||
19 | #include <unistd.h> | ||
20 | #include <errno.h> | 18 | #include <errno.h> |
21 | #include <stdio.h> | 19 | #include <stdio.h> |
20 | #include <stdlib.h> | ||
22 | #include <string.h> | 21 | #include <string.h> |
23 | #include <sys/stat.h> | 22 | #include <sys/stat.h> |
23 | #include <unistd.h> | ||
24 | |||
24 | #include <readline/readline.h> | 25 | #include <readline/readline.h> |
25 | 26 | ||
26 | /* local includes */ | 27 | /* local includes */ |
27 | #include "vchat.h" | 28 | #include "vchat-connection.h" |
28 | #include "vchat-help.h" | 29 | #include "vchat-help.h" |
29 | #include "vchat-user.h" | 30 | #include "vchat-user.h" |
31 | #include "vchat.h" | ||
30 | 32 | ||
31 | /* version of this module */ | 33 | /* version of this module */ |
32 | const char *vchat_cm_version = "vchat-commands.c $Id$"; | 34 | const char *vchat_cm_version = |
35 | "vchat-commands.c $Id$"; | ||
33 | 36 | ||
34 | /* from vchat-client.c */ | 37 | /* from vchat-client.c */ |
35 | extern int ownquit; | 38 | extern int ownquit; |
@@ -38,45 +41,45 @@ extern int status; | |||
38 | 41 | ||
39 | /* our "/command " table */ | 42 | /* our "/command " table */ |
40 | enum { | 43 | enum { |
41 | COMMAND_VERSION, | 44 | COMMAND_VERSION, |
42 | COMMAND_FILTERS, | 45 | COMMAND_FILTERS, |
43 | COMMAND_LSFLT, | 46 | COMMAND_LSFLT, |
44 | COMMAND_RMFLT, | 47 | COMMAND_RMFLT, |
45 | COMMAND_CLFLT, | 48 | COMMAND_CLFLT, |
46 | COMMAND_HELP, | 49 | COMMAND_HELP, |
47 | COMMAND_FORMAT, | 50 | COMMAND_FORMAT, |
48 | COMMAND_KEYS, | 51 | COMMAND_KEYS, |
49 | COMMAND_QUIT, | 52 | COMMAND_QUIT, |
50 | COMMAND_USER, | 53 | COMMAND_USER, |
51 | COMMAND_DICT, | 54 | COMMAND_DICT, |
52 | COMMAND_FLT, | 55 | COMMAND_FLT, |
53 | COMMAND_PM, | 56 | COMMAND_PM, |
54 | COMMAND_ACTION, | 57 | COMMAND_ACTION, |
55 | COMMAND_PMSHORT, | 58 | COMMAND_PMSHORT, |
56 | COMMAND_QUERY, | 59 | COMMAND_QUERY, |
57 | COMMAND_QUITSHORT, | 60 | COMMAND_QUITSHORT, |
58 | COMMAND_PLAIN, | 61 | COMMAND_PLAIN, |
59 | COMMAND_RECONNECT, | 62 | COMMAND_RECONNECT, |
60 | COMMAND_NONE | 63 | COMMAND_NONE |
61 | }; | 64 | }; |
62 | 65 | ||
63 | static void command_quit ( char *tail); | 66 | static void command_quit(char *tail); |
64 | static void command_user ( char *tail); | 67 | static void command_user(char *tail); |
65 | static void command_pm ( char *tail); | 68 | static void command_pm(char *tail); |
66 | static void command_action ( char *tail); | 69 | static void command_action(char *tail); |
67 | static void command_help ( char *tail); | 70 | static void command_help(char *tail); |
68 | static void command_format ( char *tail); | 71 | static void command_format(char *tail); |
69 | static void command_flt ( char *tail); | 72 | static void command_flt(char *tail); |
70 | static void command_lsflt ( char *tail); | 73 | static void command_lsflt(char *tail); |
71 | static void command_clflt ( char *tail); | 74 | static void command_clflt(char *tail); |
72 | static void command_rmflt ( char *tail); | 75 | static void command_rmflt(char *tail); |
73 | void command_version ( char *tail); | 76 | void command_version(char *tail); |
74 | static void command_none ( char *line); | 77 | static void command_none(char *line); |
75 | static void command_query ( char *tail); | 78 | static void command_query(char *tail); |
76 | static void command_reconnect ( char *tail); | 79 | static void command_reconnect(char *tail); |
77 | static void command_dict ( char *tail); | 80 | static void command_dict(char *tail); |
78 | 81 | ||
79 | static void output_default ( char *tail); | 82 | static void output_default(char *tail); |
80 | 83 | ||
81 | /* commandentry defined in vchat.h */ | 84 | /* commandentry defined in vchat.h */ |
82 | 85 | ||
@@ -105,9 +108,7 @@ commandtable[] = { | |||
105 | }; | 108 | }; |
106 | 109 | ||
107 | /* parse "/command" */ | 110 | /* parse "/command" */ |
108 | static int | 111 | static int translatecommand(char **cmd) { |
109 | translatecommand( char **cmd) | ||
110 | { | ||
111 | int result; | 112 | int result; |
112 | int cut = 0; | 113 | int cut = 0; |
113 | int maxcut = 0; | 114 | int maxcut = 0; |
@@ -115,214 +116,215 @@ translatecommand( char **cmd) | |||
115 | /* We do only want to allow Command abbrevation to | 116 | /* We do only want to allow Command abbrevation to |
116 | the next newline, so that /VRES won't expand to /V RES */ | 117 | the next newline, so that /VRES won't expand to /V RES */ |
117 | 118 | ||
118 | while( (*cmd)[maxcut] && ((*cmd)[maxcut] != 0x20) && ((*cmd)[maxcut] != '\n')) maxcut++; | 119 | while ((*cmd)[maxcut] && ((*cmd)[maxcut] != 0x20) && ((*cmd)[maxcut] != '\n')) |
119 | if( maxcut ) maxcut--; | 120 | maxcut++; |
121 | if (maxcut) | ||
122 | maxcut--; | ||
120 | 123 | ||
121 | /* Repeatedly scan command table for command, with growing abbrevation cut off */ | 124 | /* Repeatedly scan command table for command, with growing abbrevation cut off |
125 | */ | ||
122 | do { | 126 | do { |
123 | /* Looks ugly, needs rewrite for better understanding */ | 127 | /* Looks ugly, needs rewrite for better understanding */ |
124 | for( result = 0; | 128 | for (result = 0; |
125 | (result != COMMAND_NONE) && | 129 | (result != COMMAND_NONE) && |
126 | (strncasecmp(*cmd, commandtable[result].name, commandtable[result].len - | 130 | (strncasecmp( |
127 | ((commandtable[result].len - maxcut - cut > 0) ? cut : 0))); | 131 | *cmd, commandtable[result].name, |
128 | result++); | 132 | commandtable[result].len - |
129 | } while ((cut < commandtable[0].len) && (commandtable[result].number == COMMAND_NONE) && (++cut)); | 133 | ((commandtable[result].len - maxcut - cut > 0) ? cut : 0))); |
134 | result++) | ||
135 | ; | ||
136 | } while ((cut < commandtable[0].len) && | ||
137 | (commandtable[result].number == COMMAND_NONE) && (++cut)); | ||
130 | 138 | ||
131 | /* Just leave the tail... */ | 139 | /* Just leave the tail... */ |
132 | (*cmd) += commandtable[result].len; | 140 | (*cmd) += commandtable[result].len; |
133 | 141 | ||
134 | /* ... whose start may be affected by abbrevation */ | 142 | /* ... whose start may be affected by abbrevation */ |
135 | if( commandtable[result].number != COMMAND_NONE ) | 143 | if (commandtable[result].number != COMMAND_NONE) |
136 | (*cmd) -= cut; | 144 | (*cmd) -= cut; |
137 | 145 | ||
138 | return result; | 146 | return result; |
139 | } | 147 | } |
140 | 148 | ||
141 | /* handle thought */ | 149 | /* handle thought */ |
142 | static void | 150 | static void dothink(const char *tail, const char nice) { |
143 | dothink( char *tail, char nice ) | 151 | while (*tail == ' ') |
144 | { | 152 | tail++; |
145 | while( *tail == ' ' ) tail++; | ||
146 | 153 | ||
147 | /* send users message to server */ | 154 | /* send users message to server */ |
148 | snprintf (tmpstr, TMPSTRSIZE, ".%c %s", nice, tail); | 155 | snprintf(tmpstr, TMPSTRSIZE, ".%c %s", nice, tail); |
149 | networkoutput (tmpstr); | 156 | vc_sendmessage(tmpstr); |
150 | 157 | ||
151 | /* show action in channel window */ | 158 | /* show action in channel window */ |
152 | snprintf (tmpstr, TMPSTRSIZE, nice == 'O' ? getformatstr(FS_TXPUBNTHOUGHT) : getformatstr(FS_TXPUBTHOUGHT), tail); | 159 | snprintf(tmpstr, TMPSTRSIZE, |
153 | writechan (tmpstr); | 160 | nice == 'O' ? getformatstr(FS_TXPUBNTHOUGHT) |
161 | : getformatstr(FS_TXPUBTHOUGHT), | ||
162 | tail); | ||
163 | writechan(tmpstr); | ||
154 | } | 164 | } |
155 | 165 | ||
156 | |||
157 | /* handle action */ | 166 | /* handle action */ |
158 | static void | 167 | static void doaction(const char *tail) { |
159 | doaction( char *tail ) | 168 | while (*tail == ' ') |
160 | { | 169 | tail++; |
161 | while( *tail == ' ' ) tail++; | 170 | |
162 | 171 | if (*tail) { | |
163 | if( *tail ) { | 172 | /* send users message to server */ |
164 | /* send users message to server */ | 173 | snprintf(tmpstr, TMPSTRSIZE, ".a %s", tail); |
165 | snprintf (tmpstr, TMPSTRSIZE, ".a %s", tail); | 174 | vc_sendmessage(tmpstr); |
166 | networkoutput (tmpstr); | 175 | |
167 | 176 | /* show action in channel window */ | |
168 | /* show action in channel window */ | 177 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_TXPUBACTION), own_nick_get(), |
169 | snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_TXPUBACTION), own_nick_get(), tail); | 178 | tail); |
170 | writechan (tmpstr); | 179 | writechan(tmpstr); |
171 | } else { | 180 | } else { |
172 | /* missing action */ | 181 | /* missing action */ |
173 | msgout( " You do nothing. " ); | 182 | msgout(" You do nothing. "); |
174 | } | 183 | } |
175 | } | 184 | } |
176 | 185 | ||
177 | /* handle private message outgoing */ | 186 | /* handle private message outgoing */ |
178 | static void | 187 | static void privatemessagetx(char *tail) { |
179 | privatemessagetx ( char *tail ) { | ||
180 | char *mesg; | 188 | char *mesg; |
181 | 189 | ||
182 | /* find nick */ | 190 | /* find nick */ |
183 | while( *tail==' ') tail++; | 191 | while (*tail == ' ') |
192 | tail++; | ||
184 | 193 | ||
185 | /* find message */ | 194 | /* find message */ |
186 | mesg = tail; | 195 | mesg = tail; |
187 | while ( *mesg && *mesg!=' ') mesg++; | 196 | while (*mesg && *mesg != ' ') |
197 | mesg++; | ||
188 | 198 | ||
189 | /* check for nick && message */ | 199 | /* check for nick && message */ |
190 | if(*tail && *mesg) { | 200 | if (*tail && *mesg) { |
191 | 201 | ||
192 | /* terminate nick, move to rel start */ | 202 | /* terminate nick, move to rel start */ |
193 | *mesg++ = '\0'; | 203 | *mesg++ = '\0'; |
194 | 204 | ||
195 | /* form message and send to server */ | 205 | /* form message and send to server */ |
196 | snprintf (tmpstr, TMPSTRSIZE, ".m %s %s", tail, mesg); | 206 | snprintf(tmpstr, TMPSTRSIZE, ".m %s %s", tail, mesg); |
197 | networkoutput (tmpstr); | 207 | vc_sendmessage(tmpstr); |
198 | 208 | ||
199 | /* show message in private window */ | 209 | /* show message in private window */ |
200 | snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_TXPRIVMSG), tail, mesg); | 210 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_TXPRIVMSG), tail, mesg); |
201 | writepriv (tmpstr, 0); | 211 | writepriv(tmpstr, 0); |
202 | 212 | ||
203 | /* note we messaged someone */ | 213 | /* note we messaged someone */ |
204 | ul_private_action(tail); | 214 | ul_private_action(tail); |
205 | 215 | ||
206 | } else { | 216 | } else { |
207 | /* Bump user to fill in missing parts */ | 217 | /* Bump user to fill in missing parts */ |
208 | msgout( *tail ? " Won't send empty message. ":" Recipient missing. " ); | 218 | msgout(*tail ? " Won't send empty message. " : " Recipient missing. "); |
209 | } | 219 | } |
210 | } | 220 | } |
211 | 221 | ||
212 | /* handle line entered by user */ | 222 | /* handle line entered by user */ |
213 | void | 223 | void handleline(char *line) { |
214 | handleline (char *line) | ||
215 | { | ||
216 | #ifdef DEBUG | 224 | #ifdef DEBUG |
217 | /* debugging? log users input! */ | 225 | /* debugging? log users input! */ |
218 | fprintf (stderr, "=| %s\n", line); | 226 | fprintf(stderr, "=| %s\n", line); |
219 | #endif | 227 | #endif |
220 | 228 | ||
221 | switch ( line[0] ) | 229 | switch (line[0]) { |
222 | { | ||
223 | case '.': | 230 | case '.': |
224 | switch ( line[1] ) { | 231 | switch (line[1]) { |
225 | case 'm': /* sending a private message? */ | 232 | case 'm': /* sending a private message? */ |
226 | privatemessagetx( line+2 ); | 233 | privatemessagetx(line + 2); |
227 | break; | 234 | break; |
228 | case 'a': /* Do an action */ | 235 | case 'a': /* Do an action */ |
229 | doaction( line+2 ); | 236 | doaction(line + 2); |
230 | break; | 237 | break; |
231 | case '.': | 238 | case '.': |
232 | /* .. on start of line is public */ | 239 | /* .. on start of line is public */ |
233 | if( line[2] != 'm' ) { | 240 | if (line[2] != 'm') { |
234 | output_default( line ); | 241 | output_default(line); |
235 | } else { | 242 | } else { |
236 | /* oopsi, "..m " detected */ | 243 | /* oopsi, "..m " detected */ |
237 | /* dunno what to do */ | 244 | /* dunno what to do */ |
238 | flushout( ); | 245 | flushout(); |
239 | writeout("? You probably misstyped ?"); | 246 | writeout("? You probably misstyped ?"); |
240 | writeout(" "); | 247 | writeout(" "); |
241 | writeout(line ); | 248 | writeout(line); |
242 | showout( ); | 249 | showout(); |
243 | } | ||
244 | break; | ||
245 | case 'o': | ||
246 | case 'O': | ||
247 | dothink( line + 2, line[1] ); | ||
248 | break; | ||
249 | case 'x': | ||
250 | /* inform vchat-client, that the following connection | ||
251 | drop was intentional */ | ||
252 | ownquit = 1; /* fallthrough intended */ | ||
253 | default: | ||
254 | /* generic server command, send to server, show to user */ | ||
255 | snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_COMMAND), line); | ||
256 | writechan (tmpstr); | ||
257 | networkoutput (line); | ||
258 | break; | ||
259 | } | 250 | } |
260 | break; | 251 | break; |
261 | case '/': | 252 | case 'o': |
262 | line++; | 253 | case 'O': |
263 | commandtable[translatecommand(&line)].handler(line); | 254 | dothink(line + 2, line[1]); |
264 | break; | 255 | break; |
265 | default: | 256 | case 'x': |
266 | output_default( line ); | 257 | /* inform vchat-client, that the following connection |
258 | drop was intentional */ | ||
259 | ownquit = 1; /* fallthrough intended */ | ||
260 | default: | ||
261 | /* generic server command, send to server, show to user */ | ||
262 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_COMMAND), line); | ||
263 | writechan(tmpstr); | ||
264 | vc_sendmessage(line); | ||
267 | break; | 265 | break; |
266 | } | ||
267 | break; | ||
268 | case '/': | ||
269 | line++; | ||
270 | commandtable[translatecommand(&line)].handler(line); | ||
271 | break; | ||
272 | default: | ||
273 | output_default(line); | ||
274 | break; | ||
268 | } | 275 | } |
269 | } | 276 | } |
270 | 277 | ||
271 | static void | 278 | static void output_default(char *line) { |
272 | output_default(char *line ) { | 279 | /* prepare for output on display */ |
273 | /* prepare for output on display */ | 280 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_TXPUBMSG), own_nick_get(), line); |
274 | snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_TXPUBMSG), own_nick_get(), line); | ||
275 | 281 | ||
276 | /* send original line to server */ | 282 | /* send original line to server */ |
277 | networkoutput (line); | 283 | vc_sendmessage(line); |
278 | 284 | ||
279 | /* output message to channel window */ | 285 | /* output message to channel window */ |
280 | writechan (tmpstr); | 286 | writechan(tmpstr); |
281 | } | 287 | } |
282 | 288 | ||
283 | /* handle a "/user " request */ | 289 | /* handle a "/user " request */ |
284 | static void | 290 | static void command_user(char *tail) { |
285 | command_user(char *tail) | 291 | while (*tail == ' ') |
286 | { | 292 | tail++; |
287 | while( *tail == ' ') tail++; | 293 | if (*tail) { |
288 | if( *tail ) { | 294 | char *out = ul_match_user(tail); |
289 | char * out = ul_match_user( tail); | 295 | if (*out) { |
290 | if( *out ) { | 296 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_USMATCH), tail, out); |
291 | snprintf( tmpstr, TMPSTRSIZE, getformatstr(FS_USMATCH), tail, out); | 297 | } else { |
292 | } else { | 298 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_ERR), |
293 | snprintf( tmpstr, TMPSTRSIZE, getformatstr(FS_ERR), " No user matched that regex. "); | 299 | " No user matched that regex. "); |
294 | } | 300 | } |
295 | } else { | 301 | } else { |
296 | snprintf( tmpstr, TMPSTRSIZE, getformatstr(FS_ERR), " Which user? "); | 302 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_ERR), " Which user? "); |
297 | } | 303 | } |
298 | msgout( tmpstr ); | 304 | msgout(tmpstr); |
299 | } | 305 | } |
300 | 306 | ||
301 | /* handle a "/msg " request */ | 307 | /* handle a "/msg " request */ |
302 | static void | 308 | static void command_pm(char *tail) { privatemessagetx(tail); } |
303 | command_pm (char *tail) | ||
304 | { | ||
305 | privatemessagetx( tail ); | ||
306 | } | ||
307 | 309 | ||
308 | static void | 310 | static void command_format(char *line) { |
309 | command_format(char *line) { | ||
310 | struct stat testexist; | 311 | struct stat testexist; |
311 | char * tildex = NULL; | 312 | char *tildex = NULL; |
312 | 313 | ||
313 | flushout(); | 314 | flushout(); |
314 | while( *line==' ') line++; | 315 | while (*line == ' ') |
315 | if(line) { | 316 | line++; |
316 | tildex = tilde_expand( line ); | 317 | if (line) { |
317 | if(tildex && !stat(tildex, &testexist )) | 318 | tildex = tilde_expand(line); |
319 | if (tildex && !stat(tildex, &testexist)) | ||
318 | loadformats(tildex); | 320 | loadformats(tildex); |
319 | else { | 321 | else { |
320 | #define BUFSIZE 4096 | 322 | #define BUFSIZE 4096 |
321 | char buf[BUFSIZE]; | 323 | char buf[BUFSIZE]; |
322 | snprintf( buf, BUFSIZE, "~/.vchat/sample-%s.fmt", line ); | 324 | snprintf(buf, BUFSIZE, "~/.vchat/sample-%s.fmt", line); |
323 | free(tildex); | 325 | free(tildex); |
324 | tildex = tilde_expand( line ); | 326 | tildex = tilde_expand(line); |
325 | if(tildex && !stat(tildex, &testexist )) | 327 | if (tildex && !stat(tildex, &testexist)) |
326 | loadformats(tildex); | 328 | loadformats(tildex); |
327 | } | 329 | } |
328 | writeout(" Sort of done. "); | 330 | writeout(" Sort of done. "); |
@@ -334,106 +336,101 @@ command_format(char *line) { | |||
334 | } | 336 | } |
335 | 337 | ||
336 | /* handle a help request */ | 338 | /* handle a help request */ |
337 | static void | 339 | static void command_help(char *line) { |
338 | command_help (char *line) { | 340 | flushout(); |
339 | flushout( ); | 341 | while (*line == ' ') |
340 | while( *line==' ') line++; | 342 | line++; |
341 | if( *line ) { /* Get help on command */ | 343 | if (*line) { /* Get help on command */ |
342 | int i; | 344 | int i; |
343 | if( ( i = translatecommand( &line ) ) != COMMAND_NONE ) { | 345 | if ((i = translatecommand(&line)) != COMMAND_NONE) { |
344 | snprintf( tmpstr, TMPSTRSIZE, "Help on command: %s", commandtable[i].name); | 346 | snprintf(tmpstr, TMPSTRSIZE, "Help on command: %s", commandtable[i].name); |
345 | writeout( tmpstr ); | 347 | writeout(tmpstr); |
346 | writeout(" "); | 348 | writeout(" "); |
347 | if( commandtable[i].short_help && !commandtable[i].help ) | 349 | if (commandtable[i].short_help && !commandtable[i].help) |
348 | writeout(commandtable[i].short_help ); | 350 | writeout(commandtable[i].short_help); |
349 | line = commandtable[i].help; | 351 | line = commandtable[i].help; |
350 | if( line ) { | 352 | if (line) { |
351 | while( *line ) { | 353 | while (*line) { |
352 | char *tmp = tmpstr; | 354 | char *tmp = tmpstr; |
353 | while( *line && (*line != '\n') ) | 355 | while (*line && (*line != '\n')) |
354 | *tmp++ = *line++; | 356 | *tmp++ = *line++; |
355 | *tmp = '\0'; if( *line == '\n') line++; | 357 | *tmp = '\0'; |
356 | writeout ( tmpstr ); | 358 | if (*line == '\n') |
357 | } | 359 | line++; |
358 | } | 360 | writeout(tmpstr); |
359 | } else { | 361 | } |
360 | command_help( " " ); | ||
361 | } | ||
362 | } else { /* Get overall help */ | ||
363 | int i; | ||
364 | for( i = 0; commandtable[i].number != COMMAND_NONE; i++ ) { | ||
365 | if( commandtable[i].short_help ) | ||
366 | writeout( commandtable[i].short_help ); | ||
367 | } | 362 | } |
363 | } else { | ||
364 | command_help(" "); | ||
365 | } | ||
366 | } else { /* Get overall help */ | ||
367 | int i; | ||
368 | for (i = 0; commandtable[i].number != COMMAND_NONE; i++) { | ||
369 | if (commandtable[i].short_help) | ||
370 | writeout(commandtable[i].short_help); | ||
371 | } | ||
368 | } | 372 | } |
369 | showout(); | 373 | showout(); |
370 | } | 374 | } |
371 | 375 | ||
372 | /* handle an unknown command */ | 376 | /* handle an unknown command */ |
373 | static void | 377 | static void command_none(char *line) { |
374 | command_none(char *line) { | 378 | snprintf(tmpstr, TMPSTRSIZE, " Unknown client command: %s ", line); |
375 | snprintf(tmpstr, TMPSTRSIZE, " Unknown client command: %s ", line); | 379 | msgout(tmpstr); |
376 | msgout(tmpstr); | ||
377 | } | 380 | } |
378 | 381 | ||
379 | /* handle a "/flt " request */ | 382 | /* handle a "/flt " request */ |
380 | static void | 383 | static void command_flt(char *tail) { |
381 | command_flt(char *tail){ | ||
382 | char colour; | 384 | char colour; |
383 | while(*tail==' ') tail++; | 385 | while (*tail == ' ') |
386 | tail++; | ||
384 | colour = *tail++; | 387 | colour = *tail++; |
385 | while( colour && *tail == ' ') tail++; | 388 | while (colour && *tail == ' ') |
386 | if( colour && *tail) { | 389 | tail++; |
387 | addfilter( colour, tail); | 390 | if (colour && *tail) { |
391 | addfilter(colour, tail); | ||
388 | } | 392 | } |
389 | } | 393 | } |
390 | 394 | ||
391 | /* handle a "/clflt " request */ | 395 | /* handle a "/clflt " request */ |
392 | static void | 396 | static void command_clflt(char *tail) { |
393 | command_clflt (char *tail) { | 397 | while (*tail == ' ') |
394 | while( *tail == ' ') tail++; | 398 | tail++; |
395 | clearfilters( *tail ); | 399 | clearfilters(*tail); |
396 | } | 400 | } |
397 | 401 | ||
398 | /* handle a "/rmflt " request */ | 402 | /* handle a "/rmflt " request */ |
399 | static void | 403 | static void command_rmflt(char *tail) { |
400 | command_rmflt (char *tail) { | 404 | while (*tail == ' ') |
401 | while( *tail == ' ') tail++; | 405 | tail++; |
402 | removefilter( tail ); | 406 | removefilter(tail); |
403 | } | 407 | } |
404 | 408 | ||
405 | /* list filters */ | 409 | /* list filters */ |
406 | static void | 410 | static void command_lsflt(char *tail) { |
407 | command_lsflt (char *tail) { | 411 | (void)tail; |
408 | listfilters(); | 412 | listfilters(); |
409 | } | 413 | } |
410 | 414 | ||
411 | /* handle a "/me " action */ | 415 | /* handle a "/me " action */ |
412 | static void | 416 | static void command_action(char *tail) { doaction(tail); } |
413 | command_action(char *tail) | ||
414 | { | ||
415 | doaction( tail); | ||
416 | } | ||
417 | 417 | ||
418 | /* handle a "/reconnect" request */ | 418 | /* handle a "/reconnect" request */ |
419 | static void | 419 | static void command_reconnect(char *tail) { |
420 | command_reconnect(char *tail) | 420 | (void)tail; |
421 | { | ||
422 | status = 0; | 421 | status = 0; |
423 | wantreconnect = 1; | 422 | wantreconnect = 1; |
424 | ownquit = 0; | 423 | ownquit = 0; |
425 | } | 424 | } |
426 | 425 | ||
427 | /* handle a "/quit " exit */ | 426 | /* handle a "/quit " exit */ |
428 | static void | 427 | static void command_quit(char *tail) { |
429 | command_quit(char *tail) | ||
430 | { | ||
431 | /* send users message to server */ | 428 | /* send users message to server */ |
432 | snprintf (tmpstr, TMPSTRSIZE, ".x %s", tail); | 429 | snprintf(tmpstr, TMPSTRSIZE, ".x %s", tail); |
433 | networkoutput (tmpstr); | 430 | vc_sendmessage(tmpstr); |
434 | 431 | ||
435 | /* show action in channel window */ | 432 | /* show action in channel window */ |
436 | writechan (tmpstr); | 433 | writechan(tmpstr); |
437 | 434 | ||
438 | /* Inform vchat-client, that the closing connection | 435 | /* Inform vchat-client, that the closing connection |
439 | following is intended */ | 436 | following is intended */ |
@@ -442,41 +439,35 @@ command_quit(char *tail) | |||
442 | } | 439 | } |
443 | 440 | ||
444 | /* print out version */ | 441 | /* print out version */ |
445 | void | 442 | void command_version(char *tail) { |
446 | command_version(char *tail) | 443 | (void)tail; |
447 | { | ||
448 | /* output internal versions of all modules */ | 444 | /* output internal versions of all modules */ |
449 | flushout(); | 445 | flushout(); |
450 | writeout (vchat_cl_version); | 446 | writeout(vchat_cl_version); |
451 | writeout (vchat_ui_version); | 447 | writeout(vchat_ui_version); |
452 | writeout (vchat_io_version); | 448 | writeout(vchat_io_version); |
453 | writeout (vchat_us_version); | 449 | writeout(vchat_us_version); |
454 | writeout (vchat_cm_version); | 450 | writeout(vchat_cm_version); |
455 | writeout (vchat_ssl_version); | 451 | writeout(vchat_tls_version); |
456 | writeout (vchat_ssl_version_external); | 452 | writeout(vchat_tls_version_external()); |
457 | showout(); | 453 | showout(); |
458 | } | 454 | } |
459 | 455 | ||
460 | /* start or end a query */ | 456 | /* start or end a query */ |
461 | void | 457 | void command_query(char *tail) { |
462 | command_query(char *tail) | ||
463 | { | ||
464 | char *msg; | 458 | char *msg; |
465 | while( *tail == ' ') tail++; | 459 | while (*tail == ' ') |
460 | tail++; | ||
466 | 461 | ||
467 | // Check, if a message is to be sent in first query | 462 | // Check, if a message is to be sent in first query |
468 | // Note: this is safe, since readline chops trailing spaces | 463 | // Note: this is safe, since readline chops trailing spaces |
469 | if((msg = strchr(tail, ' '))) { | 464 | if ((msg = strchr(tail, ' '))) { |
470 | privatemessagetx( tail ); | 465 | privatemessagetx(tail); |
471 | *msg = 0; | 466 | *msg = 0; |
472 | } | 467 | } |
473 | 468 | ||
474 | // Do the ui stuff for query | 469 | // Do the ui stuff for query |
475 | handlequery( tail ); | 470 | handlequery(tail); |
476 | } | 471 | } |
477 | 472 | ||
478 | void | 473 | void command_dict(char *tail) { ul_add_to_dict(tail); } |
479 | command_dict(char *tail) | ||
480 | { | ||
481 | ul_add_to_dict(tail); | ||
482 | } | ||
diff --git a/vchat-config.h b/vchat-config.h index f7123d7..24365dc 100755..100644 --- a/vchat-config.h +++ b/vchat-config.h | |||
@@ -29,19 +29,21 @@ extern unsigned int hscroll; | |||
29 | static volatile configoption configoptions[] = { | 29 | static volatile configoption configoptions[] = { |
30 | /* config-option type name in file default value value localvar */ | 30 | /* config-option type name in file default value value localvar */ |
31 | {CF_NICK, CO_STR, "nick", NULL, NULL, { NULL } }, | 31 | {CF_NICK, CO_STR, "nick", NULL, NULL, { NULL } }, |
32 | {CF_FROM, CO_STR, "from", "vc-alpha-0.19", NULL, { NULL } }, | 32 | {CF_FROM, CO_STR, "from", "vc-alpha-0.20", NULL, { NULL } }, |
33 | {CF_SERVERHOST, CO_STR, "host", "localhost", NULL, { NULL } }, | 33 | {CF_SERVERHOST, CO_STR, "host", "localhost", NULL, { NULL } }, |
34 | {CF_SERVERPORT, CO_STR, "port", "2325", NULL, { NULL } }, | 34 | {CF_SERVERPORT, CO_STR, "port", "2325", NULL, { NULL } }, |
35 | {CF_CIPHERSUITE, CO_STR, "ciphers", NULL, NULL, { NULL } }, | 35 | {CF_CIPHERSUITE, CO_STR, "ciphers", NULL, NULL, { NULL } }, |
36 | {CF_CONFIGFILE, CO_STR, "conffile", "~/.vchat/config", NULL, { NULL } }, | 36 | {CF_CONFIGFILE, CO_STR, "conffile", "~/.vchat/config", NULL, { NULL } }, |
37 | {CF_CERTFILE, CO_STR, "certfile", "~/.vchat/cert", NULL, { NULL } }, | 37 | {CF_CERTFILE, CO_STR, "certfile", "~/.vchat/cert", NULL, { NULL } }, |
38 | {CF_KEYFILE, CO_STR, "keyfile", "~/.vchat/key", NULL, { NULL } }, | 38 | {CF_KEYFILE, CO_STR, "keyfile", "~/.vchat/key", NULL, { NULL } }, |
39 | {CF_CAFILE, CO_STR, "cafile", "~/.vchat/ca", NULL, { NULL } }, | ||
39 | {CF_FORMFILE, CO_STR, "formatfile", "~/.vchat/formats", NULL, { NULL } }, | 40 | {CF_FORMFILE, CO_STR, "formatfile", "~/.vchat/formats", NULL, { NULL } }, |
40 | {CF_LOGINSCRIPT, CO_STR, "loginscript","~/.vchat/loginscript", NULL, { NULL } }, | 41 | {CF_LOGINSCRIPT, CO_STR, "loginscript","~/.vchat/loginscript", NULL, { NULL } }, |
41 | {CF_FINGERPRINT, CO_STR, "fingerprint","~/.vchat/fingerprint", NULL, { NULL } }, | 42 | {CF_FINGERPRINT, CO_STR, "fingerprint","~/.vchat/fingerprint", NULL, { NULL } }, |
42 | {CF_PINFINGER, CO_INT, "pinfinger", (char *) 0, (char *)-1, { NULL } }, | 43 | {CF_PINFINGER, CO_INT, "pinfinger", (char *) 0, (char *)-1, { NULL } }, |
43 | {CF_ENCODING, CO_STR, "encoding", NULL, NULL, { .pstr = &encoding }}, | 44 | {CF_ENCODING, CO_STR, "encoding", NULL, NULL, { .pstr = &encoding }}, |
44 | {CF_USESSL, CO_INT, "usessl", (char *) 1, (char *)-1, { NULL } }, | 45 | {CF_USESSL, CO_INT, "usessl", (char *) 1, (char *)-1, { NULL } }, |
46 | {CF_TLSLIB, CO_STR, "tlslib", "mbedtls", NULL, { NULL } }, | ||
45 | {CF_IGNSSL, CO_INT, "ignssl", (char *) 0, (char *)-1, { NULL } }, | 47 | {CF_IGNSSL, CO_INT, "ignssl", (char *) 0, (char *)-1, { NULL } }, |
46 | {CF_VERIFYSSL, CO_INT, "verifyssl", (char *) 2, (char *)-1, { NULL } }, | 48 | {CF_VERIFYSSL, CO_INT, "verifyssl", (char *) 2, (char *)-1, { NULL } }, |
47 | {CF_USECERT, CO_INT, "usecert", (char *) 1, (char *)-1, { NULL } }, | 49 | {CF_USECERT, CO_INT, "usecert", (char *) 1, (char *)-1, { NULL } }, |
@@ -49,6 +51,7 @@ static volatile configoption configoptions[] = { | |||
49 | {CF_USETOPIC, CO_INT, "usetopicbar",(char *) 1, (char *)-1, { NULL } }, | 51 | {CF_USETOPIC, CO_INT, "usetopicbar",(char *) 1, (char *)-1, { NULL } }, |
50 | {CF_PRIVHEIGHT, CO_INT, "messages", (char *) 0, (char *)-1, { NULL } }, | 52 | {CF_PRIVHEIGHT, CO_INT, "messages", (char *) 0, (char *)-1, { NULL } }, |
51 | {CF_PRIVCOLLAPS, CO_INT, "privcollaps",(char *) 0, (char *)-1, { NULL } }, | 53 | {CF_PRIVCOLLAPS, CO_INT, "privcollaps",(char *) 0, (char *)-1, { NULL } }, |
54 | {CF_INVWINBAR, CO_INT, "invwinbar", (char *) 0, (char *)-1, { NULL } }, | ||
52 | {CF_HSCROLL, CO_INT, "hscroll", (char *)15, (char *)-1, { .pint = &hscroll } }, | 55 | {CF_HSCROLL, CO_INT, "hscroll", (char *)15, (char *)-1, { .pint = &hscroll } }, |
53 | {CF_CHANNEL, CO_INT, "channel", (char *) 0, (char *)-1, { NULL } }, | 56 | {CF_CHANNEL, CO_INT, "channel", (char *) 0, (char *)-1, { NULL } }, |
54 | {CF_SCROLLBPRIV, CO_INT, "privscrollb",(char *) 2048, (char *)-1, { NULL } }, | 57 | {CF_SCROLLBPRIV, CO_INT, "privscrollb",(char *) 2048, (char *)-1, { NULL } }, |
diff --git a/vchat-connection.c b/vchat-connection.c new file mode 100644 index 0000000..dea69d0 --- /dev/null +++ b/vchat-connection.c | |||
@@ -0,0 +1,350 @@ | |||
1 | /* | ||
2 | * vchat-client - alpha version | ||
3 | * vchat-connection.c - handling of server connection and tls library dispatch | ||
4 | * | ||
5 | * Copyright (C) 2022 Dirk Engling <erdgeist@erdgeist.org> | ||
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 | #include <errno.h> | ||
18 | #include <netdb.h> | ||
19 | #include <netinet/in.h> | ||
20 | #include <stddef.h> | ||
21 | #include <stdio.h> | ||
22 | #include <stdlib.h> | ||
23 | #include <string.h> | ||
24 | #include <sys/socket.h> | ||
25 | #include <sys/types.h> | ||
26 | #include <unistd.h> | ||
27 | |||
28 | /* For tilde_expand */ | ||
29 | #include <readline/readline.h> | ||
30 | |||
31 | #include "vchat-connection.h" | ||
32 | #include "vchat-tls.h" | ||
33 | #include "vchat.h" | ||
34 | |||
35 | static int serverfd = -1; | ||
36 | unsigned int want_tcp_keepalive = 0; | ||
37 | |||
38 | enum { TLS_ENGINE_UNSET, TLS_ENGINE_OPENSSL, TLS_ENGINE_MBEDTLS }; | ||
39 | static int _engine = TLS_ENGINE_UNSET; | ||
40 | |||
41 | #define STAGING_SIZE 16384 | ||
42 | #define RECEIVEBUF_SIZE 4096 | ||
43 | |||
44 | /* Generic tcp connector, blocking */ | ||
45 | static int connect_tcp_socket(const char *server, const char *port) { | ||
46 | struct addrinfo hints, *res, *res0; | ||
47 | int s, error; | ||
48 | |||
49 | memset(&hints, 0, sizeof(hints)); | ||
50 | hints.ai_family = PF_UNSPEC; | ||
51 | hints.ai_socktype = SOCK_STREAM; | ||
52 | error = getaddrinfo(server, port, &hints, &res0); | ||
53 | if (error) | ||
54 | return -1; | ||
55 | s = -1; | ||
56 | for (res = res0; res; res = res->ai_next) { | ||
57 | s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); | ||
58 | if (s < 0) | ||
59 | continue; | ||
60 | if (connect(s, res->ai_addr, res->ai_addrlen) < 0) { | ||
61 | close(s); | ||
62 | s = -1; | ||
63 | continue; | ||
64 | } | ||
65 | break; /* okay we got one */ | ||
66 | } | ||
67 | freeaddrinfo(res0); | ||
68 | |||
69 | if (want_tcp_keepalive) { | ||
70 | int one = 1; | ||
71 | setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof(one)); | ||
72 | } | ||
73 | return s; | ||
74 | } | ||
75 | |||
76 | /* Return a tilde expanded path in a malloced buffer or NULL */ | ||
77 | static char *get_tilde_expanded(confopt opt) { | ||
78 | char *str = getstroption(opt); | ||
79 | if (!str) | ||
80 | return str; | ||
81 | if (str[0] == '~') | ||
82 | return tilde_expand(str); | ||
83 | return strdup(str); | ||
84 | } | ||
85 | |||
86 | /* connects to server */ | ||
87 | int vc_connect(const char *server, const char *port) { | ||
88 | /* vchat connection x509 store */ | ||
89 | vc_x509store_t vc_store; | ||
90 | |||
91 | /* pointer to tilde-expanded certificate/keyfile-names */ | ||
92 | char *certfile, *cafile; | ||
93 | int result = -1, want_openssl = !strcmp(getstroption(CF_TLSLIB), "openssl"); | ||
94 | |||
95 | /* Connect to the server */ | ||
96 | serverfd = connect_tcp_socket(server, port); | ||
97 | if (serverfd < 0) { | ||
98 | /* inform user */ | ||
99 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_CANTCONNECT), server, port); | ||
100 | writechan(tmpstr); | ||
101 | return -1; | ||
102 | } | ||
103 | |||
104 | if (!getintoption(CF_USESSL)) | ||
105 | return 0; | ||
106 | |||
107 | #ifdef TLS_LIB_OPENSSL | ||
108 | _engine = TLS_ENGINE_OPENSSL; | ||
109 | #endif | ||
110 | #ifdef TLS_LIB_MBEDTLS | ||
111 | /* Make mbedtls default unless mbedtls is configured */ | ||
112 | if (!want_openssl || _engine == TLS_ENGINE_UNSET) | ||
113 | _engine = TLS_ENGINE_MBEDTLS; | ||
114 | #endif | ||
115 | |||
116 | if (_engine == TLS_ENGINE_UNSET) { | ||
117 | writecf(FS_ERR, "Error: tls requested but no tls engine compiled in."); | ||
118 | return -1; | ||
119 | } | ||
120 | |||
121 | if (want_openssl && _engine == TLS_ENGINE_MBEDTLS) | ||
122 | writecf(FS_SERV, "Warning: tls engine openssl requested but openssl engine " | ||
123 | "not compiled in. Using mbedtls"); | ||
124 | |||
125 | if (!want_openssl && _engine == TLS_ENGINE_OPENSSL) | ||
126 | writecf(FS_SERV, "Warning: tls engine mbedtls requested but mbedts engine " | ||
127 | "not compiled in. Using openssl"); | ||
128 | |||
129 | /* If SSL is requested, get our ssl-BIO running */ | ||
130 | #ifdef TLS_LIB_OPENSSL | ||
131 | if (_engine == TLS_ENGINE_OPENSSL) | ||
132 | vc_openssl_init_x509store(&vc_store); | ||
133 | #endif | ||
134 | #ifdef TLS_LIB_MBEDTLS | ||
135 | if (_engine == TLS_ENGINE_MBEDTLS) | ||
136 | vc_mbedtls_init_x509store(&vc_store); | ||
137 | #endif | ||
138 | |||
139 | /* get name of certificate file */ | ||
140 | certfile = get_tilde_expanded(CF_CERTFILE); | ||
141 | /* do we have a certificate file? */ | ||
142 | if (certfile) { | ||
143 | /* get name of key file */ | ||
144 | char *keyfile = get_tilde_expanded(CF_KEYFILE); | ||
145 | |||
146 | vc_x509store_setcertfile(&vc_store, certfile); | ||
147 | vc_x509store_set_pkeycb(&vc_store, (vc_askpass_cb_t)passprompt); | ||
148 | |||
149 | /* if we don't have a key file, the key may be in the cert file */ | ||
150 | vc_x509store_setkeyfile(&vc_store, keyfile ? keyfile : certfile); | ||
151 | |||
152 | free(keyfile); | ||
153 | free(certfile); | ||
154 | } | ||
155 | |||
156 | /* get name of ca file */ | ||
157 | cafile = get_tilde_expanded(CF_CAFILE); | ||
158 | if (cafile && !access(cafile, F_OK)) | ||
159 | vc_x509store_setcafile(&vc_store, cafile); | ||
160 | free(cafile); | ||
161 | |||
162 | /* upgrade our plain BIO to ssl */ | ||
163 | #ifdef TLS_LIB_OPENSSL | ||
164 | if (_engine == TLS_ENGINE_OPENSSL) | ||
165 | result = vc_openssl_connect(serverfd, &vc_store); | ||
166 | #endif | ||
167 | #ifdef TLS_LIB_MBEDTLS | ||
168 | if (_engine == TLS_ENGINE_MBEDTLS) | ||
169 | result = vc_mbedtls_connect(serverfd, &vc_store); | ||
170 | #endif | ||
171 | vc_cleanup_x509store(&vc_store); | ||
172 | |||
173 | if (result) { | ||
174 | close(serverfd); | ||
175 | serverfd = -1; | ||
176 | errno = EIO; | ||
177 | #ifdef TLS_LIB_OPENSSL | ||
178 | if (_engine == TLS_ENGINE_OPENSSL) | ||
179 | vc_openssl_cleanup(); | ||
180 | #endif | ||
181 | #ifdef TLS_LIB_MBEDTLS | ||
182 | if (_engine == TLS_ENGINE_MBEDTLS) | ||
183 | vc_mbedtls_cleanup(); | ||
184 | #endif | ||
185 | |||
186 | _engine = TLS_ENGINE_UNSET; | ||
187 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_CANTCONNECT), server, port); | ||
188 | writechan(tmpstr); | ||
189 | return -1; | ||
190 | } | ||
191 | |||
192 | /* inform user */ | ||
193 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_CONNECTED), server, port); | ||
194 | writechan(tmpstr); | ||
195 | |||
196 | #ifdef DEBUG | ||
197 | dumpfile = fopen("dumpfile", "a"); | ||
198 | #endif | ||
199 | |||
200 | /* if we didn't fail until now, we've got a connection. */ | ||
201 | return 0; | ||
202 | } | ||
203 | |||
204 | /* Poll for activity on the socket or stdin */ | ||
205 | int vc_poll(int timeout_seconds) { | ||
206 | fd_set readfds; | ||
207 | FD_ZERO(&readfds); | ||
208 | FD_SET(0, &readfds); | ||
209 | if (serverfd != -1) | ||
210 | FD_SET(serverfd, &readfds); | ||
211 | struct timeval tv = {timeout_seconds, 0}; | ||
212 | int result = select(serverfd + 2, &readfds, NULL, NULL, &tv); | ||
213 | if (result <= 0) | ||
214 | return result; | ||
215 | result = FD_ISSET(0, &readfds) ? 1 : 0; | ||
216 | if (serverfd != -1) | ||
217 | result += FD_ISSET(serverfd, &readfds) ? 2 : 0; | ||
218 | return result; | ||
219 | } | ||
220 | |||
221 | /* disconnect from server */ | ||
222 | void vc_disconnect() { | ||
223 | if (serverfd > 0) { | ||
224 | close(serverfd); | ||
225 | serverfd = -1; | ||
226 | } | ||
227 | #ifdef TLS_LIB_OPENSSL | ||
228 | if (_engine == TLS_ENGINE_OPENSSL) | ||
229 | vc_openssl_cleanup(); | ||
230 | #endif | ||
231 | #ifdef TLS_LIB_MBEDTLS | ||
232 | if (_engine == TLS_ENGINE_MBEDTLS) | ||
233 | vc_mbedtls_cleanup(); | ||
234 | #endif | ||
235 | |||
236 | _engine = TLS_ENGINE_UNSET; | ||
237 | loggedin = 0; | ||
238 | } | ||
239 | |||
240 | void vc_sendmessage(const char *msg) { | ||
241 | static char staging[STAGING_SIZE]; | ||
242 | size_t sent = 0, len = snprintf(staging, sizeof(staging), "%s\r\n", msg); | ||
243 | #ifdef DEBUG | ||
244 | /* debugging? log network output! */ | ||
245 | fprintf(dumpfile, ">| (%zd) %s\n", len - 2, msg); | ||
246 | #endif | ||
247 | |||
248 | if (getintoption(CF_USESSL)) { | ||
249 | #ifdef TLS_LIB_OPENSSL | ||
250 | if (_engine == TLS_ENGINE_OPENSSL) | ||
251 | sent = vc_openssl_sendmessage(staging, len); | ||
252 | #endif | ||
253 | #ifdef TLS_LIB_MBEDTLS | ||
254 | if (_engine == TLS_ENGINE_MBEDTLS) | ||
255 | sent = vc_mbedtls_sendmessage(staging, len); | ||
256 | #endif | ||
257 | } else | ||
258 | sent = write(serverfd, staging, len); | ||
259 | if (sent != len) | ||
260 | writecf(FS_ERR, "Message sending fuzzy."); | ||
261 | } | ||
262 | |||
263 | /* get data from servers connection */ | ||
264 | int vc_receive(void) { | ||
265 | /* offset in buffer (for linebreaks at packet borders) */ | ||
266 | static char buf[RECEIVEBUF_SIZE]; | ||
267 | static size_t buf_fill; | ||
268 | char *endmsg; | ||
269 | size_t freebytes = sizeof(buf) - buf_fill; | ||
270 | ssize_t bytes = 0; | ||
271 | |||
272 | if (!getintoption(CF_USESSL)) | ||
273 | bytes = read(serverfd, buf + buf_fill, freebytes); | ||
274 | else | ||
275 | #ifdef TLS_LIB_OPENSSL | ||
276 | if (_engine == TLS_ENGINE_OPENSSL) | ||
277 | bytes = vc_openssl_receivemessage(buf + buf_fill, freebytes); | ||
278 | #endif | ||
279 | #ifdef TLS_LIB_MBEDTLS | ||
280 | if (_engine == TLS_ENGINE_MBEDTLS) | ||
281 | bytes = vc_mbedtls_receivemessage(buf + buf_fill, freebytes); | ||
282 | #endif | ||
283 | |||
284 | /* Our tls functions may require retries with handshakes etc, this is | ||
285 | * signalled by -2 */ | ||
286 | if (bytes == -2) | ||
287 | return 0; | ||
288 | |||
289 | /* Error on the socket read? raise error message, bail out */ | ||
290 | if (bytes == -1) { | ||
291 | snprintf(tmpstr, TMPSTRSIZE, "Receive fails, %s.", strerror(errno)); | ||
292 | snprintf(errstr, ERRSTRSIZE, "Receive fails, %s.\n", strerror(errno)); | ||
293 | writecf(FS_ERR, tmpstr); | ||
294 | return -1; | ||
295 | } | ||
296 | |||
297 | /* end of file from server? */ | ||
298 | if (bytes == 0) { | ||
299 | /* inform user, bail out */ | ||
300 | writecf(FS_SERV, "* EOF from server."); | ||
301 | snprintf(errstr, ERRSTRSIZE, "* EOF from server.\n"); | ||
302 | return -1; | ||
303 | } | ||
304 | |||
305 | buf_fill += bytes; | ||
306 | |||
307 | /* as long as there are lines .. */ | ||
308 | while ((endmsg = memchr(buf, '\n', buf_fill)) != NULL) { | ||
309 | if (endmsg > buf) { | ||
310 | /* Zero terminate message, optionally chomp CR */ | ||
311 | endmsg[0] = 0; | ||
312 | if (endmsg[-1] == '\r') | ||
313 | endmsg[-1] = 0; | ||
314 | /* If terminating and chomping left us with a message, give it to line | ||
315 | * handler */ | ||
316 | if (buf[0]) { | ||
317 | #ifdef DEBUG | ||
318 | /* debugging? log network input! */ | ||
319 | fprintf(stderr, "<| %s\n", buf); | ||
320 | #endif | ||
321 | protocol_parsemsg(buf); | ||
322 | } | ||
323 | } | ||
324 | buf_fill -= 1 + endmsg - buf; | ||
325 | memmove(buf, endmsg + 1, buf_fill); | ||
326 | } | ||
327 | return 0; | ||
328 | } | ||
329 | |||
330 | const char *vchat_tls_version_external() { | ||
331 | #ifdef TLS_LIB_OPENSSL | ||
332 | char *openssl_version = vc_openssl_version(); | ||
333 | #else | ||
334 | char *openssl_version = strdup("not installed"); | ||
335 | #endif | ||
336 | #ifdef TLS_LIB_MBEDTLS | ||
337 | char *mbedtls_version = vc_mbedtls_version(); | ||
338 | #else | ||
339 | char *mbedtls_version = strdup("not installed"); | ||
340 | #endif | ||
341 | |||
342 | snprintf(tmpstr, TMPSTRSIZE, | ||
343 | "Module plain v0.1\nModule openssl version: %s\nModule mbedtls " | ||
344 | "version: %s", | ||
345 | openssl_version, mbedtls_version); | ||
346 | |||
347 | free(openssl_version); | ||
348 | free(mbedtls_version); | ||
349 | return tmpstr; | ||
350 | } | ||
diff --git a/vchat-connection.h b/vchat-connection.h new file mode 100644 index 0000000..84294f6 --- /dev/null +++ b/vchat-connection.h | |||
@@ -0,0 +1,11 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include <stdint.h> | ||
4 | |||
5 | int vc_connect(const char *host, const char *port); | ||
6 | void vc_sendmessage(const char *message); | ||
7 | int vc_receive(); | ||
8 | int vc_poll(int); | ||
9 | void vc_disconnect(); | ||
10 | |||
11 | const char *vchat_tls_version_external(); | ||
diff --git a/vchat-help.h b/vchat-help.h index a7964fd..a7964fd 100755..100644 --- a/vchat-help.h +++ b/vchat-help.h | |||
diff --git a/vchat-howto b/vchat-howto index 27f8e14..27f8e14 100755..100644 --- a/vchat-howto +++ b/vchat-howto | |||
diff --git a/vchat-keygen b/vchat-keygen index 91fcbba..4163838 100755 --- a/vchat-keygen +++ b/vchat-keygen | |||
@@ -29,7 +29,8 @@ if [ ! -e $KEYBASE.key ]; then | |||
29 | echo "vchat-keygen: generating RSA key $KEYBASE.key" | 29 | echo "vchat-keygen: generating RSA key $KEYBASE.key" |
30 | echo "vchat-keygen: please set passphrase for local security" | 30 | echo "vchat-keygen: please set passphrase for local security" |
31 | umask 0077 | 31 | umask 0077 |
32 | openssl genrsa -des3 -out $KEYBASE.key 4096 | 32 | openssl ecparam -genkey -name secp384r1 | \ |
33 | openssl ec -out $KEYBASE.key -aes256 | ||
33 | else | 34 | else |
34 | echo "vchat-keygen: private key $KEYBASE.key exists" | 35 | echo "vchat-keygen: private key $KEYBASE.key exists" |
35 | fi | 36 | fi |
@@ -40,11 +41,11 @@ fi | |||
40 | echo "vchat-keygen: generating config-file for self-signing $KEYBASE.ca.keyconf" | 41 | echo "vchat-keygen: generating config-file for self-signing $KEYBASE.ca.keyconf" |
41 | cat >$KEYBASE.ca.keyconf <<EOT | 42 | cat >$KEYBASE.ca.keyconf <<EOT |
42 | [ req ] | 43 | [ req ] |
43 | default_bits = 4096 | ||
44 | default_keyfile = user.key | 44 | default_keyfile = user.key |
45 | distinguished_name = req_distinguished_name | 45 | distinguished_name = req_distinguished_name |
46 | string_mask = nombstr | 46 | string_mask = nombstr |
47 | req_extensions = v3_req | 47 | req_extensions = v3_req |
48 | default_md = sha384 | ||
48 | [ req_distinguished_name ] | 49 | [ req_distinguished_name ] |
49 | commonName = Name | 50 | commonName = Name |
50 | commonName_max = 64 | 51 | commonName_max = 64 |
@@ -57,7 +58,7 @@ EOT | |||
57 | fi | 58 | fi |
58 | echo "vchat-keygen: generating Certificate Signing Request $KEYBASE.csr" | 59 | echo "vchat-keygen: generating Certificate Signing Request $KEYBASE.csr" |
59 | echo "vchat-keygen: please enter your nickname at the 'Name []:' prompt" | 60 | echo "vchat-keygen: please enter your nickname at the 'Name []:' prompt" |
60 | openssl req -new -sha1 -config $KEYBASE.ca.keyconf -key $KEYBASE.key -out $KEYBASE.csr | 61 | openssl req -new -sha256 -config $KEYBASE.ca.keyconf -key $KEYBASE.key -out $KEYBASE.csr |
61 | echo "vchat-keygen: send this ($KEYBASE.csr) Certificate Signing Request to | 62 | echo "vchat-keygen: send this ($KEYBASE.csr) Certificate Signing Request to |
62 | vchat@vchat.berlin.ccc.de to get it signed by the vchat-CA. You will | 63 | vchat@vchat.berlin.ccc.de to get it signed by the vchat-CA. You will |
63 | receive your signed Certificate shortly." | 64 | receive your signed Certificate shortly." |
diff --git a/vchat-messages.h b/vchat-messages.h index 0902e88..0902e88 100755..100644 --- a/vchat-messages.h +++ b/vchat-messages.h | |||
diff --git a/vchat-protocol.c b/vchat-protocol.c index f1b71a1..c5021e4 100755..100644 --- a/vchat-protocol.c +++ b/vchat-protocol.c | |||
@@ -1,6 +1,6 @@ | |||
1 | /* | 1 | /* |
2 | * vchat-client - alpha version | 2 | * vchat-client - alpha version |
3 | * vchat-protocol.c - handling of server connection & messages | 3 | * vchat-protocol.c - handling of server messages |
4 | * | 4 | * |
5 | * Copyright (C) 2001 Andreas Kotes <count@flatline.de> | 5 | * Copyright (C) 2001 Andreas Kotes <count@flatline.de> |
6 | * | 6 | * |
@@ -10,528 +10,367 @@ | |||
10 | * without even the implied warranty of merchantability or fitness for a | 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 | 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 | 12 | * any direct, indirect, incidental or special damages arising in any way out |
13 | * of the use of this software. | 13 | * of the use of this software. |
14 | * | 14 | * |
15 | */ | 15 | */ |
16 | 16 | ||
17 | /* general includes */ | 17 | /* general includes */ |
18 | #include <unistd.h> | ||
19 | #include <errno.h> | 18 | #include <errno.h> |
19 | #include <langinfo.h> | ||
20 | #include <locale.h> | ||
20 | #include <stdio.h> | 21 | #include <stdio.h> |
21 | #include <netdb.h> | 22 | #include <stdlib.h> |
22 | #include <string.h> | 23 | #include <string.h> |
23 | #include <sys/types.h> | 24 | |
24 | #include <sys/socket.h> | ||
25 | #include <netinet/in.h> | ||
26 | #include <readline/readline.h> | 25 | #include <readline/readline.h> |
27 | #include <locale.h> | ||
28 | #include <langinfo.h> | ||
29 | 26 | ||
30 | // TO BE GONE | 27 | #ifdef DEBUG |
31 | #include <openssl/bio.h> | 28 | FILE *dumpfile; |
29 | #endif | ||
32 | 30 | ||
33 | /* local includes */ | 31 | /* local includes */ |
34 | #include "vchat.h" | 32 | #include "vchat-connection.h" |
35 | #include "vchat-user.h" | 33 | #include "vchat-user.h" |
36 | #include "vchat-ssl.h" | 34 | #include "vchat.h" |
37 | 35 | ||
38 | /* version of this module */ | 36 | /* version of this module */ |
39 | const char *vchat_io_version = "vchat-protocol.c $Id$"; | 37 | const char *vchat_io_version = |
40 | 38 | "vchat-protocol.c $Id$"; | |
41 | /* externally used variables */ | ||
42 | int serverfd = -1; | ||
43 | |||
44 | /* locally global variables */ | ||
45 | /* our connection BIO */ | ||
46 | static BIO *server_conn = NULL; | ||
47 | 39 | ||
48 | /* declaration of local helper functions */ | 40 | /* declaration of local helper functions */ |
49 | static void usersignon (char *); | 41 | static void usersignon(char *); |
50 | static void usersignoff (char *); | 42 | static void usersignoff(char *); |
51 | static void usernickchange (char *); | 43 | static void usernickchange(char *); |
52 | static void userjoin (char *); | 44 | static void userjoin(char *); |
53 | static void userleave (char *); | 45 | static void userleave(char *); |
54 | static void receivenicks (char *message); | 46 | static void receivenicks(char *message); |
55 | static void justloggedin (char *message); | 47 | static void justloggedin(char *message); |
56 | static void nickerr (char *message); | 48 | static void nickerr(char *message); |
57 | static void login (char *message); | 49 | static void login(char *message); |
58 | static void anonlogin (char *message); | 50 | static void anonlogin(char *message); |
59 | static void topicinfo (char *message); | 51 | static void topicinfo(char *message); |
60 | static void pubaction (char *message); | 52 | static void pubaction(char *message); |
61 | static void pubthoughts (char *message); | 53 | static void pubthoughts(char *message); |
62 | static void serverlogin (char *message); | 54 | static void serverlogin(char *message); |
63 | static void idleprompt (char *message); | 55 | static void idleprompt(char *message); |
64 | static void topicchange (char *message); | 56 | static void topicchange(char *message); |
65 | static void pmnotsent (char *message); | 57 | static void pmnotsent(char *message); |
66 | 58 | ||
67 | /* declaration of server message array */ | 59 | /* declaration of server message array */ |
68 | #include "vchat-messages.h" | 60 | #include "vchat-messages.h" |
69 | 61 | ||
70 | /* status-variable from vchat-client.c | ||
71 | * eventloop is done as long as this is true */ | ||
72 | extern int status; | ||
73 | char *encoding; | 62 | char *encoding; |
74 | 63 | ||
75 | static int connect_socket( char *server, char *port ) { | ||
76 | struct addrinfo hints, *res, *res0; | ||
77 | int s, error; | ||
78 | |||
79 | memset(&hints, 0, sizeof(hints)); | ||
80 | hints.ai_family = PF_UNSPEC; | ||
81 | hints.ai_socktype = SOCK_STREAM; | ||
82 | error = getaddrinfo( server, port, &hints, &res0 ); | ||
83 | if (error) return -1; | ||
84 | s = -1; | ||
85 | for (res = res0; res; res = res->ai_next) { | ||
86 | s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); | ||
87 | if (s < 0) continue; | ||
88 | if (connect(s, res->ai_addr, res->ai_addrlen) < 0) { | ||
89 | close(s); | ||
90 | s = -1; | ||
91 | continue; | ||
92 | } | ||
93 | break; /* okay we got one */ | ||
94 | } | ||
95 | freeaddrinfo(res0); | ||
96 | |||
97 | if (want_tcp_keepalive) { /* global from vchat-client.c */ | ||
98 | int one=1; | ||
99 | setsockopt(s,SOL_SOCKET,SO_KEEPALIVE,&one,sizeof(one)); | ||
100 | } | ||
101 | return s; | ||
102 | } | ||
103 | |||
104 | /* connects to server */ | ||
105 | int | ||
106 | vcconnect (char *server, char *port) | ||
107 | { | ||
108 | /* used for tilde expansion of cert & key filenames */ | ||
109 | char *tildex = NULL; | ||
110 | |||
111 | /* vchat connection x509 store */ | ||
112 | vc_x509store_t *vc_store; | ||
113 | |||
114 | /* pointer to tilde-expanded certificate/keyfile-names */ | ||
115 | char *certfile = NULL, *keyfile = NULL; | ||
116 | |||
117 | /* Connect to the server */ | ||
118 | serverfd = connect_socket( server, port ); | ||
119 | if( serverfd < 0 ) { | ||
120 | /* inform user */ | ||
121 | snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_CANTCONNECT), server, port ); | ||
122 | writechan (tmpstr); | ||
123 | return -1; | ||
124 | } | ||
125 | /* Abstract server IO in openssls BIO */ | ||
126 | server_conn = BIO_new_socket( serverfd, 1 ); | ||
127 | |||
128 | /* If SSL is requested, get our ssl-BIO running */ | ||
129 | if( server_conn && getintoption(CF_USESSL) ) { | ||
130 | vc_store = vc_init_x509store(); | ||
131 | if( !vc_store ) { | ||
132 | snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_ERR), "Out of memory" ); | ||
133 | writechan (tmpstr); | ||
134 | return -1; | ||
135 | } | ||
136 | |||
137 | vc_x509store_setflags(vc_store, VC_X509S_SSL_VERIFY_PEER); | ||
138 | |||
139 | /* get name of certificate file */ | ||
140 | certfile = getstroption (CF_CERTFILE); | ||
141 | /* do we have a certificate file? */ | ||
142 | if (certfile) { | ||
143 | /* does the filename start with a tilde? expand it! */ | ||
144 | if (certfile[0] == '~') | ||
145 | tildex = tilde_expand (certfile); | ||
146 | else | ||
147 | tildex = certfile; | ||
148 | |||
149 | vc_x509store_setflags(vc_store, VC_X509S_USE_CERTIFICATE); | ||
150 | vc_x509store_setcertfile(vc_store, tildex); | ||
151 | |||
152 | /* get name of key file */ | ||
153 | keyfile = getstroption (CF_KEYFILE); | ||
154 | |||
155 | /* if we don't have a key file, the key may be in the cert file */ | ||
156 | if (!keyfile) | ||
157 | keyfile = certfile; | ||
158 | |||
159 | /* does the filename start with a tilde? expand it! */ | ||
160 | if (keyfile[0] == '~') | ||
161 | tildex = tilde_expand (keyfile); | ||
162 | else | ||
163 | tildex = keyfile; | ||
164 | |||
165 | vc_x509store_set_pkeycb(vc_store, (vc_askpass_cb_t)passprompt); | ||
166 | vc_x509store_setkeyfile(vc_store, tildex); | ||
167 | } | ||
168 | |||
169 | /* upgrade our plain BIO to ssl */ | ||
170 | if( vc_connect_ssl( &server_conn, vc_store ) ) { | ||
171 | BIO_free_all( server_conn ); | ||
172 | server_conn = NULL; | ||
173 | errno = EIO; | ||
174 | } | ||
175 | } | ||
176 | |||
177 | if( !server_conn ) { | ||
178 | /* inform user */ | ||
179 | snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_CANTCONNECT), server, port ); | ||
180 | writechan (tmpstr); | ||
181 | return -1; | ||
182 | } | ||
183 | |||
184 | /* inform user */ | ||
185 | snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_CONNECTED), server, port); | ||
186 | writechan (tmpstr); | ||
187 | |||
188 | /* if we didn't fail until now, we've got a connection. */ | ||
189 | return 0; | ||
190 | } | ||
191 | |||
192 | /* disconnect from server */ | ||
193 | void | ||
194 | vcdisconnect () { | ||
195 | BIO_free_all( server_conn ); | ||
196 | server_conn = 0; | ||
197 | if (serverfd>0) { | ||
198 | close(serverfd); | ||
199 | serverfd = -1; | ||
200 | } | ||
201 | } | ||
202 | |||
203 | /* handle a pm not sent error | 64 | /* handle a pm not sent error |
204 | * format: 412 %s */ | 65 | * format: 412 %s */ |
205 | static void | 66 | static void pmnotsent(char *message) { |
206 | pmnotsent (char *message) | 67 | while (*message && *message != ' ') |
207 | { | 68 | message++; |
208 | while(*message && *message!=' ') message++; | 69 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_ERR), message + 1); |
209 | snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_ERR),message+1); | 70 | writepriv(tmpstr, 0); |
210 | writepriv( tmpstr, 0); | ||
211 | |||
212 | } | 71 | } |
213 | 72 | ||
214 | /* parse and handle an action string | 73 | /* parse and handle an action string |
215 | * format: 118 %s %s | 74 | * format: 118 %s %s |
216 | * vars: %s nick | 75 | * vars: %s nick |
217 | * %s action */ | 76 | * %s action */ |
218 | static void | 77 | static void pubaction(char *message) { |
219 | pubaction (char *message) | ||
220 | { | ||
221 | char *nick = NULL, *action = NULL; | 78 | char *nick = NULL, *action = NULL; |
222 | nick = strchr (message, ' '); | 79 | nick = strchr(message, ' '); |
223 | nick[0] = '\0'; | 80 | nick[0] = '\0'; |
224 | nick++; | 81 | nick++; |
225 | 82 | ||
226 | action = strchr (nick, ' '); | 83 | action = strchr(nick, ' '); |
227 | action[0] = '\0'; | 84 | action[0] = '\0'; |
228 | action++; | 85 | action++; |
229 | 86 | ||
230 | ul_public_action(nick); | 87 | ul_public_action(nick); |
231 | snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_PUBACTION),nick,action); | 88 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_PUBACTION), nick, action); |
232 | writechan (tmpstr); | 89 | writechan(tmpstr); |
233 | } | 90 | } |
234 | 91 | ||
235 | /* parse and handle an thought string | 92 | /* parse and handle an thought string |
236 | * format: 124 %s %s | 93 | * format: 124 %s %s |
237 | * vars: %s nick | 94 | * vars: %s nick |
238 | * %s thought */ | 95 | * %s thought */ |
239 | static void | 96 | static void pubthoughts(char *message) { |
240 | pubthoughts (char *message) | ||
241 | { | ||
242 | char *nick = NULL, *thoughts = NULL; | 97 | char *nick = NULL, *thoughts = NULL; |
243 | nick = strchr (message, ' '); | 98 | nick = strchr(message, ' '); |
244 | nick[0] = '\0'; | 99 | nick[0] = '\0'; |
245 | nick++; | 100 | nick++; |
246 | 101 | ||
247 | thoughts = strchr (nick, ' '); | 102 | thoughts = strchr(nick, ' '); |
248 | thoughts[0] = '\0'; | 103 | thoughts[0] = '\0'; |
249 | thoughts++; | 104 | thoughts++; |
250 | 105 | ||
251 | ul_public_action(nick); | 106 | ul_public_action(nick); |
252 | snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_PUBTHOUGHT),nick,thoughts); | 107 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_PUBTHOUGHT), nick, thoughts); |
253 | writechan (tmpstr); | 108 | writechan(tmpstr); |
254 | } | 109 | } |
255 | 110 | ||
256 | /* parse and handle server logon */ | 111 | /* parse and handle server logon */ |
257 | static void | 112 | static void serverlogin(char *message) { |
258 | serverlogin (char *message) | 113 | (void)message; |
259 | { | 114 | int utf8 = !strcmp(nl_langinfo(CODESET), "UTF-8"); |
260 | int utf8=!strcmp(nl_langinfo(CODESET), "UTF-8"); | ||
261 | if (utf8) | 115 | if (utf8) |
262 | networkoutput(".e utf8"); | 116 | vc_sendmessage(".e utf8"); |
263 | } | 117 | } |
264 | 118 | ||
265 | /* parse and handle an idle message | 119 | /* parse and handle an idle message |
266 | * format: 305 | 120 | * format: 305 |
267 | * vars: %s message */ | 121 | * vars: %s message */ |
268 | static void | 122 | static void idleprompt(char *message) { |
269 | idleprompt (char *message) | ||
270 | { | ||
271 | char *msg = NULL; | 123 | char *msg = NULL; |
272 | msg = strchr (message, ' '); | 124 | msg = strchr(message, ' '); |
273 | msg[0] = '\0'; | 125 | msg[0] = '\0'; |
274 | msg++; | 126 | msg++; |
275 | 127 | ||
276 | snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_IDLE),msg); | 128 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_IDLE), msg); |
277 | writechan (tmpstr); | 129 | writechan(tmpstr); |
278 | } | 130 | } |
279 | 131 | ||
280 | /* parse and handle a topic information string | 132 | /* parse and handle a topic information string |
281 | * format: 115 %d %s | 133 | * format: 115 %d %s |
282 | * vars: %d chan - channel number | 134 | * vars: %d chan - channel number |
283 | * %s topic - topic */ | 135 | * %s topic - topic */ |
284 | static void | 136 | static void topicinfo(char *message) { |
285 | topicinfo (char *message) | ||
286 | { | ||
287 | char *channel = NULL, *topic = NULL; | 137 | char *channel = NULL, *topic = NULL; |
288 | int tmpchan = 0, ownchan = own_channel_get(); | 138 | int tmpchan = 0, ownchan = own_channel_get(); |
289 | 139 | ||
290 | /* search start of channel number */ | 140 | /* search start of channel number */ |
291 | channel = strchr (message, ' '); | 141 | channel = strchr(message, ' '); |
292 | channel++; | 142 | channel++; |
293 | 143 | ||
294 | /* search start of topic and terminate channel number */ | 144 | /* search start of topic and terminate channel number */ |
295 | topic = strchr (channel, ' '); | 145 | topic = strchr(channel, ' '); |
296 | topic[0] = '\0'; | 146 | topic[0] = '\0'; |
297 | topic++; | 147 | topic++; |
298 | 148 | ||
299 | /* convert channel number to integer */ | 149 | /* convert channel number to integer */ |
300 | tmpchan = atoi (channel); | 150 | tmpchan = atoi(channel); |
301 | 151 | ||
302 | if (tmpchan == ownchan ) { | 152 | if (tmpchan == ownchan) { |
303 | /* show change in topic window */ | 153 | /* show change in topic window */ |
304 | if (strlen(topic)) | 154 | if (strlen(topic)) |
305 | snprintf (topicstr, TOPICSTRSIZE, getformatstr(FS_TOPICW), ownchan, topic); | 155 | snprintf(topicstr, TOPICSTRSIZE, getformatstr(FS_TOPICW), ownchan, topic); |
306 | else | 156 | else |
307 | snprintf (topicstr, TOPICSTRSIZE, getformatstr(FS_NOTOPICW), ownchan ); | 157 | snprintf(topicstr, TOPICSTRSIZE, getformatstr(FS_NOTOPICW), ownchan); |
308 | topicline(NULL); | 158 | topicline(NULL); |
309 | } | 159 | } |
310 | 160 | ||
311 | /* announce change in channel window */ | 161 | /* announce change in channel window */ |
312 | if (strlen(topic)) | 162 | if (strlen(topic)) |
313 | snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_TOPIC), tmpchan, topic); | 163 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_TOPIC), tmpchan, topic); |
314 | else | 164 | else |
315 | snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_NOTOPIC), tmpchan); | 165 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_NOTOPIC), tmpchan); |
316 | writechan (tmpstr); | 166 | writechan(tmpstr); |
317 | } | 167 | } |
318 | 168 | ||
319 | /* parse and handle a topic change string | 169 | /* parse and handle a topic change string |
320 | * format: 114 %s changes topic to '%s' | 170 | * format: 114 %s changes topic to '%s' |
321 | * vars: %s nick | 171 | * vars: %s nick |
322 | * %s topic */ | 172 | * %s topic */ |
323 | static void | 173 | static void topicchange(char *message) { |
324 | topicchange (char *message) | ||
325 | { | ||
326 | char *nick = NULL, *topic = NULL; | 174 | char *nick = NULL, *topic = NULL; |
327 | int len, ownchan = own_channel_get(); | 175 | int len, ownchan = own_channel_get(); |
328 | 176 | ||
329 | /* search start of nickname */ | 177 | /* search start of nickname */ |
330 | nick = strchr (message, ' '); | 178 | nick = strchr(message, ' '); |
331 | nick++; | 179 | nick++; |
332 | 180 | ||
333 | /* search start of message before topic, terminate nick */ | 181 | /* search start of message before topic, terminate nick */ |
334 | topic = strchr (nick, ' '); | 182 | topic = strchr(nick, ' '); |
335 | topic[0] = '\0'; | 183 | topic[0] = '\0'; |
336 | topic++; | 184 | topic++; |
337 | 185 | ||
338 | /* search start of real topic and terminate channel number */ | 186 | /* search start of real topic and terminate channel number */ |
339 | topic = strchr (topic, '\''); | 187 | topic = strchr(topic, '\''); |
340 | topic[0] = '\0'; | 188 | topic[0] = '\0'; |
341 | topic++; | 189 | topic++; |
342 | 190 | ||
343 | /* remove ending '\'', if there */ | 191 | /* remove ending '\'', if there */ |
344 | len = strlen (topic); | 192 | len = strlen(topic); |
345 | if (topic[len-1] == '\'') | 193 | if (topic[len - 1] == '\'') |
346 | topic[len-1] = '\0'; | 194 | topic[len - 1] = '\0'; |
347 | 195 | ||
348 | ul_public_action(nick); | 196 | ul_public_action(nick); |
349 | /* show change in topic window */ | 197 | /* show change in topic window */ |
350 | snprintf (topicstr, TOPICSTRSIZE, getformatstr(FS_TOPICW), ownchan, topic); | 198 | snprintf(topicstr, TOPICSTRSIZE, getformatstr(FS_TOPICW), ownchan, topic); |
351 | topicline(NULL); | 199 | topicline(NULL); |
352 | 200 | ||
353 | /* announce change in channel window */ | 201 | /* announce change in channel window */ |
354 | snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_CHGTOPIC), nick, topic); | 202 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_CHGTOPIC), nick, topic); |
355 | writechan (tmpstr); | 203 | writechan(tmpstr); |
356 | } | 204 | } |
357 | 205 | ||
358 | /* parse and handle a login message | 206 | /* parse and handle a login message |
359 | * format: 212 %s %s | 207 | * format: 212 %s %s |
360 | * vars: %s str1 - nick used to login | 208 | * vars: %s str1 - nick used to login |
361 | * %s str2 - servers message */ | 209 | * %s str2 - servers message */ |
362 | static void | 210 | static void justloggedin(char *message) { |
363 | justloggedin (char *message) | ||
364 | { | ||
365 | char *str1 = NULL, *str2 = NULL; | 211 | char *str1 = NULL, *str2 = NULL; |
366 | /* search start of nickname */ | 212 | /* search start of nickname */ |
367 | str1 = strchr (message, ' '); | 213 | str1 = strchr(message, ' '); |
368 | str1++; | 214 | str1++; |
369 | 215 | ||
370 | /* search start of message, terminate nick */ | 216 | /* search start of message, terminate nick */ |
371 | str2 = strchr (str1, ' '); | 217 | str2 = strchr(str1, ' '); |
372 | str2[0] = '\0'; | 218 | str2[0] = '\0'; |
373 | str2++; | 219 | str2++; |
374 | 220 | ||
375 | /* if we have a new nick, store it */ | 221 | /* if we have a new nick, store it */ |
376 | own_nick_set( str1 ); | 222 | own_nick_set(str1); |
377 | 223 | ||
378 | /* show change in console window */ | 224 | /* show change in console window */ |
379 | snprintf (consolestr, CONSOLESTRSIZE, getformatstr(FS_CONSOLE), str1, getstroption (CF_SERVERHOST), getstroption (CF_SERVERPORT)); | 225 | snprintf(consolestr, CONSOLESTRSIZE, getformatstr(FS_CONSOLE), str1, |
380 | consoleline (NULL); | 226 | getstroption(CF_SERVERHOST), getstroption(CF_SERVERPORT)); |
227 | consoleline(NULL); | ||
381 | 228 | ||
382 | /* announce login as servermessage */ | 229 | /* announce login as servermessage */ |
383 | snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_SIGNON), str1, str2); | 230 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_SIGNON), str1, str2); |
384 | writechan (tmpstr); | 231 | writechan(tmpstr); |
385 | 232 | ||
386 | /* we're not logged in, change status and request nicks */ | 233 | /* we're not logged in, change status and request nicks */ |
387 | if (!loggedin) | 234 | if (!loggedin) { |
388 | { | 235 | loadcfg(getstroption(CF_LOGINSCRIPT), 0, handleline); |
389 | loadcfg(getstroption(CF_LOGINSCRIPT),0,handleline); | 236 | handleline(".S"); |
390 | handleline(".S"); | 237 | loggedin = 1; |
391 | loggedin = 1; | 238 | flushconnect(); |
392 | } | 239 | } |
393 | } | 240 | } |
394 | 241 | ||
395 | /* this user joins a different channel */ | 242 | /* this user joins a different channel */ |
396 | void | 243 | void ownjoin(int channel) { |
397 | ownjoin (int channel) | 244 | vc_sendmessage(".t"); |
398 | { | 245 | snprintf(tmpstr, TMPSTRSIZE, ".S %d", channel); |
399 | networkoutput(".t"); | 246 | vc_sendmessage(tmpstr); |
400 | snprintf(tmpstr, TMPSTRSIZE, ".S %d",channel); | ||
401 | networkoutput(tmpstr); | ||
402 | } | 247 | } |
403 | 248 | ||
404 | /* this user changes his nick */ | 249 | /* this user changes his nick */ |
405 | void | 250 | void ownnickchange(const char *newnick) { (void)newnick; } |
406 | ownnickchange (char *newnick) | ||
407 | { | ||
408 | } | ||
409 | 251 | ||
410 | /* parse and handle a nick error message | 252 | /* parse and handle a nick error message |
411 | * format: 401 %s | 253 | * format: 401 %s |
412 | * 403 %s | 254 | * 403 %s |
413 | * 415 %s | 255 | * 415 %s |
414 | * vars: %s - server message */ | 256 | * vars: %s - server message */ |
415 | static void | 257 | static void nickerr(char *message) { |
416 | nickerr (char *message) | ||
417 | { | ||
418 | char *helpkiller = NULL; | 258 | char *helpkiller = NULL; |
419 | /* mutate message for output */ | 259 | /* mutate message for output */ |
420 | message[2] = '!'; | 260 | message[2] = '!'; |
421 | /* help information found? remove it. */ | 261 | /* help information found? remove it. */ |
422 | if ((helpkiller = strstr (message, " Type .h for help"))) | 262 | if ((helpkiller = strstr(message, " Type .h for help"))) |
423 | helpkiller[0] = '\0'; | 263 | helpkiller[0] = '\0'; |
424 | /* nickchange not done? eliminate message */ | 264 | /* nickchange not done? eliminate message */ |
425 | if (loggedin && (helpkiller = strstr (message, " - Nick not changed"))) | 265 | if (loggedin && (helpkiller = strstr(message, " - Nick not changed"))) |
426 | helpkiller[0] = '\0'; | 266 | helpkiller[0] = '\0'; |
427 | /* show errormessage */ | 267 | /* show errormessage */ |
428 | writecf (FS_ERR,&message[2]); | 268 | writecf(FS_ERR, &message[2]); |
429 | 269 | ||
430 | /* not logged in? insist on getting a new nick */ | 270 | /* not logged in? insist on getting a new nick */ |
431 | if (!loggedin) | 271 | if (!loggedin) { |
432 | { | 272 | /* free bogus nick */ |
433 | /* free bogus nick */ | 273 | own_nick_set(NULL); |
434 | own_nick_set(NULL); | ||
435 | 274 | ||
436 | /* get new nick via vchat-ui.c */ | 275 | /* get new nick via vchat-ui.c */ |
437 | nickprompt (); | 276 | nickprompt(); |
438 | 277 | ||
439 | /* form login message and send it to server */ | 278 | /* form login message and send it to server */ |
440 | snprintf (tmpstr, TMPSTRSIZE, ".l %s %s %d", own_nick_get(), getstroption (CF_FROM), getintoption (CF_CHANNEL)); | 279 | snprintf(tmpstr, TMPSTRSIZE, ".l %s %s %d", own_nick_get(), |
441 | networkoutput (tmpstr); | 280 | getstroption(CF_FROM), getintoption(CF_CHANNEL)); |
442 | } | 281 | vc_sendmessage(tmpstr); |
282 | } | ||
443 | } | 283 | } |
444 | 284 | ||
445 | /* parse and handle a registered nick information | 285 | /* parse and handle a registered nick information |
446 | * format: 120 %s %s | 286 | * format: 120 %s %s |
447 | * vars: %s - this users registered nick | 287 | * vars: %s - this users registered nick |
448 | * %s msg - server message */ | 288 | * %s msg - server message */ |
449 | static void | 289 | static void login(char *message) { |
450 | login (char *message) { | ||
451 | char *msg = NULL; | 290 | char *msg = NULL; |
452 | 291 | ||
453 | /* mutate message for output */ | 292 | /* mutate message for output */ |
454 | message[2] = '*'; | 293 | message[2] = '*'; |
455 | /* show message to user */ | 294 | /* show message to user */ |
456 | writecf (FS_SERV,&message[2]); | 295 | writecf(FS_SERV, &message[2]); |
457 | 296 | ||
458 | /* we don't know our nick? */ | 297 | /* we don't know our nick? */ |
459 | if (!own_nick_get() ) { | 298 | if (!own_nick_get()) { |
460 | /* find message after nick */ | 299 | /* find message after nick */ |
461 | msg = strchr (&message[4], ' '); | 300 | msg = strchr(&message[4], ' '); |
462 | if (msg) { | 301 | if (msg) { |
463 | /* terminate string before message and copy nick */ | 302 | /* terminate string before message and copy nick */ |
464 | msg[0] = '\0'; | 303 | msg[0] = '\0'; |
465 | own_nick_set(&message[4]); | 304 | own_nick_set(&message[4]); |
466 | } else { | 305 | } else { |
467 | /* no string in servers message (huh?), ask user for nick */ | 306 | /* no string in servers message (huh?), ask user for nick */ |
468 | nickprompt (); | 307 | nickprompt(); |
469 | } | 308 | } |
470 | } | 309 | } |
471 | 310 | ||
472 | /* form login message and send it to server */ | 311 | /* form login message and send it to server */ |
473 | snprintf (tmpstr, TMPSTRSIZE, ".l %s %s %d", own_nick_get(), getstroption (CF_FROM), getintoption (CF_CHANNEL)); | 312 | snprintf(tmpstr, TMPSTRSIZE, ".l %s %s %d", own_nick_get(), |
474 | networkoutput (tmpstr); | 313 | getstroption(CF_FROM), getintoption(CF_CHANNEL)); |
314 | vc_sendmessage(tmpstr); | ||
475 | } | 315 | } |
476 | 316 | ||
477 | /* parse and handle anon login request | 317 | /* parse and handle anon login request |
478 | * format: 121 %s | 318 | * format: 121 %s |
479 | * vars: %s - server message */ | 319 | * vars: %s - server message */ |
480 | static void | 320 | static void anonlogin(char *message) { |
481 | anonlogin (char *message) | ||
482 | { | ||
483 | /* mutate message for output */ | 321 | /* mutate message for output */ |
484 | message[2] = '*'; | 322 | message[2] = '*'; |
485 | /* show message to user */ | 323 | /* show message to user */ |
486 | writecf (FS_SERV,&message[2]); | 324 | writecf(FS_SERV, &message[2]); |
487 | 325 | ||
488 | /* we don't know our nick? ask for it! */ | 326 | /* we don't know our nick? ask for it! */ |
489 | if (!own_nick_get()) | 327 | if (!own_nick_get()) |
490 | nickprompt (); | 328 | nickprompt(); |
491 | 329 | ||
492 | /* form login message and send it to server */ | 330 | /* form login message and send it to server */ |
493 | snprintf (tmpstr, TMPSTRSIZE, ".l %s %s %d", own_nick_get(), getstroption (CF_FROM), getintoption (CF_CHANNEL)); | 331 | snprintf(tmpstr, TMPSTRSIZE, ".l %s %s %d", own_nick_get(), |
494 | networkoutput (tmpstr); | 332 | getstroption(CF_FROM), getintoption(CF_CHANNEL)); |
333 | vc_sendmessage(tmpstr); | ||
495 | } | 334 | } |
496 | 335 | ||
497 | /* parse and handle list of nicks (from '.S') | 336 | /* parse and handle list of nicks (from '.S') |
498 | * format: 119 %s .. | 337 | * format: 119 %s .. |
499 | * vars: %s nick - a users nick */ | 338 | * vars: %s nick - a users nick */ |
500 | static void | 339 | static void receivenicks(char *message) { |
501 | receivenicks (char *message) { | ||
502 | char *str1 = NULL, *str2 = NULL; | 340 | char *str1 = NULL, *str2 = NULL; |
503 | int chanflag = -1; | 341 | int chanflag = -1; |
504 | 342 | ||
505 | /* show message to user */ | 343 | /* show message to user */ |
506 | snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_USONLINE), &message[4]); | 344 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_USONLINE), &message[4]); |
507 | writechan (tmpstr); | 345 | writechan(tmpstr); |
508 | 346 | ||
509 | /* search for channelnumber */ | 347 | /* search for channelnumber */ |
510 | if( !(str1 = strchr (message, ' ') ) ) return; | 348 | if (!(str1 = strchr(message, ' '))) |
349 | return; | ||
511 | str1++; | 350 | str1++; |
512 | 351 | ||
513 | if (str1[0] == '*') { | 352 | if (str1[0] == '*') { |
514 | ul_rebuild_list(); | 353 | ul_rebuild_list(); |
515 | str1++; | 354 | str1++; |
516 | } else { | 355 | } else { |
517 | int mychan; | 356 | int mychan; |
518 | str2 = str1; | 357 | str2 = str1; |
519 | str1 = strchr(str2,' '); | 358 | str1 = strchr(str2, ' '); |
520 | str1[0] = '\0'; | 359 | str1[0] = '\0'; |
521 | mychan = atoi(str2); | 360 | mychan = atoi(str2); |
522 | if( mychan != own_channel_get() ) | 361 | if (mychan != own_channel_get()) |
523 | return; | 362 | return; |
524 | 363 | ||
525 | /* Kick all users from the IN_MY_CHANNEL list */ | 364 | /* Kick all users from the IN_MY_CHANNEL list */ |
526 | own_channel_set( own_channel_get() ); | 365 | own_channel_set(own_channel_get()); |
527 | chanflag = 1; | 366 | chanflag = 1; |
528 | } | 367 | } |
529 | str1++; | 368 | str1++; |
530 | 369 | ||
531 | /* while user .. */ | 370 | /* while user .. */ |
532 | while (str1) { | 371 | while (str1) { |
533 | /* search next user */ | 372 | /* search next user */ |
534 | str2 = strchr (str1, ' '); | 373 | str2 = strchr(str1, ' '); |
535 | /* there is another user? terminate this one */ | 374 | /* there is another user? terminate this one */ |
536 | if (str2) { | 375 | if (str2) { |
537 | str2[0] = '\0'; | 376 | str2[0] = '\0'; |
@@ -551,52 +390,48 @@ receivenicks (char *message) { | |||
551 | * format: 211 %s %s | 390 | * format: 211 %s %s |
552 | * vars: %s nick - who logged on | 391 | * vars: %s nick - who logged on |
553 | * %s msg - servers message */ | 392 | * %s msg - servers message */ |
554 | static void | 393 | static void usersignon(char *message) { |
555 | usersignon (char *message) | ||
556 | { | ||
557 | char *nick = NULL, *msg = NULL; | 394 | char *nick = NULL, *msg = NULL; |
558 | /* search start of nickname */ | 395 | /* search start of nickname */ |
559 | nick = strchr (message, ' '); | 396 | nick = strchr(message, ' '); |
560 | nick++; | 397 | nick++; |
561 | 398 | ||
562 | /* search start of message, terminate nick */ | 399 | /* search start of message, terminate nick */ |
563 | msg = strchr (nick, ' '); | 400 | msg = strchr(nick, ' '); |
564 | msg[0] = '\0'; | 401 | msg[0] = '\0'; |
565 | msg++; | 402 | msg++; |
566 | 403 | ||
567 | /* add this user via vchat-user.c */ | 404 | /* add this user via vchat-user.c */ |
568 | ul_add (nick, 0); | 405 | ul_add(nick, 0); |
569 | 406 | ||
570 | /* show message to user */ | 407 | /* show message to user */ |
571 | snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_SIGNON), nick, msg); | 408 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_SIGNON), nick, msg); |
572 | writechan (tmpstr); | 409 | writechan(tmpstr); |
573 | } | 410 | } |
574 | 411 | ||
575 | /* parse and handle a logoff message | 412 | /* parse and handle a logoff message |
576 | * format: 221 %s %s | 413 | * format: 221 %s %s |
577 | * vars: %s nick - who logged off | 414 | * vars: %s nick - who logged off |
578 | * %s msg - servers message */ | 415 | * %s msg - servers message */ |
579 | static void | 416 | static void usersignoff(char *message) { |
580 | usersignoff (char *message) | ||
581 | { | ||
582 | char *nick = NULL, *msg = NULL; | 417 | char *nick = NULL, *msg = NULL; |
583 | /* search start of nickname */ | 418 | /* search start of nickname */ |
584 | nick = strchr (message, ' '); | 419 | nick = strchr(message, ' '); |
585 | nick++; | 420 | nick++; |
586 | 421 | ||
587 | /* search start of message, terminate nick */ | 422 | /* search start of message, terminate nick */ |
588 | msg = strchr (nick, ' '); | 423 | msg = strchr(nick, ' '); |
589 | if( msg ) { | 424 | if (msg) { |
590 | msg[0] = '\0'; | 425 | msg[0] = '\0'; |
591 | msg++; | 426 | msg++; |
592 | } | 427 | } |
593 | 428 | ||
594 | /* delete this user via vchat-user.c */ | 429 | /* delete this user via vchat-user.c */ |
595 | ul_del (nick); | 430 | ul_del(nick); |
596 | 431 | ||
597 | /* show message to user */ | 432 | /* show message to user */ |
598 | snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_SIGNOFF), nick, msg); | 433 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_SIGNOFF), nick, msg); |
599 | writechan (tmpstr); | 434 | writechan(tmpstr); |
600 | } | 435 | } |
601 | 436 | ||
602 | /* parse and handle a join message | 437 | /* parse and handle a join message |
@@ -604,40 +439,38 @@ usersignoff (char *message) | |||
604 | * vars: %s nick - who joined | 439 | * vars: %s nick - who joined |
605 | * %s msg - servers message | 440 | * %s msg - servers message |
606 | * %d chan - channel joined */ | 441 | * %d chan - channel joined */ |
607 | static void | 442 | static void userjoin(char *message) { |
608 | userjoin (char *message) | ||
609 | { | ||
610 | char *nick = NULL, *msg = NULL, *channel = NULL; | 443 | char *nick = NULL, *msg = NULL, *channel = NULL; |
611 | int chan = 0; | 444 | int chan = 0; |
612 | 445 | ||
613 | /* search start of nickname */ | 446 | /* search start of nickname */ |
614 | nick = strchr (message, ' '); | 447 | nick = strchr(message, ' '); |
615 | nick++; | 448 | nick++; |
616 | 449 | ||
617 | /* search start of message, terminate nick */ | 450 | /* search start of message, terminate nick */ |
618 | msg = strchr (nick, ' '); | 451 | msg = strchr(nick, ' '); |
619 | msg[0] = '\0'; | 452 | msg[0] = '\0'; |
620 | msg++; | 453 | msg++; |
621 | 454 | ||
622 | /* search start of channel, terminate message */ | 455 | /* search start of channel, terminate message */ |
623 | channel = strrchr (msg, ' '); | 456 | channel = strrchr(msg, ' '); |
624 | channel[0] = '\0'; | 457 | channel[0] = '\0'; |
625 | channel++; | 458 | channel++; |
626 | 459 | ||
627 | /* convert channel to integer */ | 460 | /* convert channel to integer */ |
628 | chan = atoi (channel); | 461 | chan = atoi(channel); |
629 | 462 | ||
630 | /* is it myself joining */ | 463 | /* is it myself joining */ |
631 | if( own_nick_check(nick) ) | 464 | if (own_nick_check(nick)) |
632 | own_channel_set(chan); | 465 | own_channel_set(chan); |
633 | 466 | ||
634 | /* notice channel join via vchat-user.c */ | 467 | /* notice channel join via vchat-user.c */ |
635 | if( own_channel_get() == chan ) | 468 | if (own_channel_get() == chan) |
636 | ul_enter_chan(nick); | 469 | ul_enter_chan(nick); |
637 | 470 | ||
638 | /* show message to user */ | 471 | /* show message to user */ |
639 | snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_JOIN), nick, msg, chan); | 472 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_JOIN), nick, msg, chan); |
640 | writechan (tmpstr); | 473 | writechan(tmpstr); |
641 | } | 474 | } |
642 | 475 | ||
643 | /* parse and handle a leave message | 476 | /* parse and handle a leave message |
@@ -645,35 +478,33 @@ userjoin (char *message) | |||
645 | * vars: %s nick - who left | 478 | * vars: %s nick - who left |
646 | * %s msg - servers message | 479 | * %s msg - servers message |
647 | * %d chan - channel joined */ | 480 | * %d chan - channel joined */ |
648 | static void | 481 | static void userleave(char *message) { |
649 | userleave (char *message) | ||
650 | { | ||
651 | char *nick = NULL, *msg = NULL, *channel = NULL; | 482 | char *nick = NULL, *msg = NULL, *channel = NULL; |
652 | int chan = 0; | 483 | int chan = 0; |
653 | 484 | ||
654 | /* search start of nickname */ | 485 | /* search start of nickname */ |
655 | nick = strchr (message, ' '); | 486 | nick = strchr(message, ' '); |
656 | nick++; | 487 | nick++; |
657 | 488 | ||
658 | /* search start of message, terminate nick */ | 489 | /* search start of message, terminate nick */ |
659 | msg = strchr (nick, ' '); | 490 | msg = strchr(nick, ' '); |
660 | msg[0] = '\0'; | 491 | msg[0] = '\0'; |
661 | msg++; | 492 | msg++; |
662 | 493 | ||
663 | /* convert channel to integer */ | 494 | /* convert channel to integer */ |
664 | channel = strrchr (msg, ' '); | 495 | channel = strrchr(msg, ' '); |
665 | channel[0] = '\0'; | 496 | channel[0] = '\0'; |
666 | channel++; | 497 | channel++; |
667 | 498 | ||
668 | /* convert channel to integer */ | 499 | /* convert channel to integer */ |
669 | chan = atoi (channel); | 500 | chan = atoi(channel); |
670 | 501 | ||
671 | /* notice channel leave via vchat-user.c */ | 502 | /* notice channel leave via vchat-user.c */ |
672 | ul_leave_chan(nick); | 503 | ul_leave_chan(nick); |
673 | 504 | ||
674 | /* show message to user */ | 505 | /* show message to user */ |
675 | snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_LEAVE), nick, msg, chan); | 506 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_LEAVE), nick, msg, chan); |
676 | writechan (tmpstr); | 507 | writechan(tmpstr); |
677 | } | 508 | } |
678 | 509 | ||
679 | /* parse and handle a nickchange message | 510 | /* parse and handle a nickchange message |
@@ -681,242 +512,140 @@ userleave (char *message) | |||
681 | * vars: %s oldnick - users old nick | 512 | * vars: %s oldnick - users old nick |
682 | * %s newnick - users new nick | 513 | * %s newnick - users new nick |
683 | * %s msg - server message */ | 514 | * %s msg - server message */ |
684 | static void | 515 | static void usernickchange(char *message) { |
685 | usernickchange (char *message) | ||
686 | { | ||
687 | char *oldnick = NULL, *newnick = NULL, *msg = NULL; | 516 | char *oldnick = NULL, *newnick = NULL, *msg = NULL; |
688 | 517 | ||
689 | /* search start of old nickname */ | 518 | /* search start of old nickname */ |
690 | oldnick = strchr (message, ' '); | 519 | oldnick = strchr(message, ' '); |
691 | oldnick++; | 520 | oldnick++; |
692 | 521 | ||
693 | /* search start of new nickname, terminate old nick */ | 522 | /* search start of new nickname, terminate old nick */ |
694 | newnick = strchr (oldnick, ' '); | 523 | newnick = strchr(oldnick, ' '); |
695 | newnick[0] = '\0'; | 524 | newnick[0] = '\0'; |
696 | newnick++; | 525 | newnick++; |
697 | 526 | ||
698 | /* search start of message, terminate new nick */ | 527 | /* search start of message, terminate new nick */ |
699 | msg = strchr (newnick, ' '); | 528 | msg = strchr(newnick, ' '); |
700 | msg[0] = '\0'; | 529 | msg[0] = '\0'; |
701 | msg++; | 530 | msg++; |
702 | 531 | ||
703 | /* notice nickchange via vchat-user.c */ | 532 | /* notice nickchange via vchat-user.c */ |
704 | ul_rename (oldnick, newnick); | 533 | ul_rename(oldnick, newnick); |
705 | 534 | ||
706 | if( own_nick_check(newnick) ) { | 535 | if (own_nick_check(newnick)) { |
707 | /* show change in console window */ | 536 | /* show change in console window */ |
708 | snprintf (consolestr, CONSOLESTRSIZE, getformatstr(FS_CONSOLE), newnick, getstroption (CF_SERVERHOST), getstroption (CF_SERVERPORT)); | 537 | snprintf(consolestr, CONSOLESTRSIZE, getformatstr(FS_CONSOLE), newnick, |
709 | consoleline (NULL); | 538 | getstroption(CF_SERVERHOST), getstroption(CF_SERVERPORT)); |
539 | consoleline(NULL); | ||
710 | } | 540 | } |
711 | 541 | ||
712 | /* show message to user */ | 542 | /* show message to user */ |
713 | snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_NICKCHANGE), oldnick, newnick, msg); | 543 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_NICKCHANGE), oldnick, newnick, |
714 | writechan (tmpstr); | 544 | msg); |
545 | writechan(tmpstr); | ||
715 | } | 546 | } |
716 | 547 | ||
717 | /* handle received message from server */ | 548 | /* handle received message from server */ |
718 | static void | 549 | void protocol_parsemsg(char *message) { |
719 | parsemsg (char *message) | ||
720 | { | ||
721 | char *str1, *str2; | 550 | char *str1, *str2; |
722 | int i; | 551 | int i; |
723 | /* message to short or starts with '<'? must be channel */ | 552 | /* message to short or starts with '<'? must be channel */ |
724 | if (message[0] == '<') | 553 | if (message[0] == '<') { |
725 | { | ||
726 | str1 = &message[1]; | 554 | str1 = &message[1]; |
727 | str2 = strchr(str1,'>'); | 555 | str2 = strchr(str1, '>'); |
728 | str2[0] = '\0'; | 556 | str2[0] = '\0'; |
729 | str2++; | 557 | str2++; |
730 | if (str2[0] == ' ') str2++; | 558 | if (str2[0] == ' ') |
559 | str2++; | ||
731 | if (own_nick_check(str1)) | 560 | if (own_nick_check(str1)) |
732 | snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_MYPUBMSG),str1,str2); | 561 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_MYPUBMSG), str1, str2); |
733 | else | 562 | else |
734 | snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_RXPUBMSG),str1,str2); | 563 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_RXPUBMSG), str1, str2); |
735 | writechan (tmpstr); | 564 | writechan(tmpstr); |
736 | 565 | ||
737 | ul_public_action(str1); | 566 | ul_public_action(str1); |
738 | } | 567 | } else if (message[0] == '[') { |
739 | else if (message[0] == '[') | ||
740 | { | ||
741 | str1 = &message[1]; | 568 | str1 = &message[1]; |
742 | str2 = strchr(str1,']'); | 569 | str2 = strchr(str1, ']'); |
743 | str2[0] = '\0'; | 570 | str2[0] = '\0'; |
744 | str2++; | 571 | str2++; |
745 | if (str2[0] == ' ') str2++; | 572 | if (str2[0] == ' ') |
746 | if (own_nick_check( str1 )) | 573 | str2++; |
747 | snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_MYPUBURL),str1,str2); | 574 | if (own_nick_check(str1)) |
575 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_MYPUBURL), str1, str2); | ||
748 | else | 576 | else |
749 | snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_RXPUBURL),str1,str2); | 577 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_RXPUBURL), str1, str2); |
750 | ul_public_action(str1); | 578 | ul_public_action(str1); |
751 | writechan (tmpstr); | 579 | writechan(tmpstr); |
752 | } | 580 | } |
753 | /* message starts with '*'? must be private */ | 581 | /* message starts with '*'? must be private */ |
754 | else if (message[0] == '*') | 582 | else if (message[0] == '*') { |
755 | { | ||
756 | str1 = &message[1]; | 583 | str1 = &message[1]; |
757 | str2 = strchr(str1,'*'); | 584 | str2 = strchr(str1, '*'); |
758 | str2[0] = '\0'; | 585 | str2[0] = '\0'; |
759 | str2++; | 586 | str2++; |
760 | if (str2[0] == ' ') str2++; | 587 | if (str2[0] == ' ') |
761 | snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_RXPRIVMSG),str1,str2); | 588 | str2++; |
762 | writepriv (tmpstr, 1); | 589 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_RXPRIVMSG), str1, str2); |
590 | writepriv(tmpstr, 1); | ||
763 | ul_private_action(str1); | 591 | ul_private_action(str1); |
764 | } | 592 | } |
765 | /* message starts with a number? must be a servermessage */ | 593 | /* message starts with a number? must be a servermessage */ |
766 | else if ((message[0] >= '0') && (message[0] <= '9')) | 594 | else if ((message[0] >= '0') && (message[0] <= '9')) { |
767 | { | 595 | /* walk server message array */ |
768 | /* walk server message array */ | 596 | for (i = 0; servermessages[i].id[0]; i++) { |
769 | for (i = 0; servermessages[i].id[0]; i++) | 597 | /* is this the message? */ |
770 | { | 598 | if (!(strncmp(servermessages[i].id, message, 3))) { |
771 | /* is this the message? */ | 599 | /* scripting hook available - call it */ |
772 | if (!(strncmp (servermessages[i].id, message, 3))) | 600 | if (servermessages[i].hook) |
773 | { | 601 | servermessages[i].hook(message); |
774 | /* scripting hook available - call it */ | 602 | /* function available - call it */ |
775 | if (servermessages[i].hook) | 603 | else if (servermessages[i].funct) |
776 | servermessages[i].hook (message); | 604 | servermessages[i].funct(message); |
777 | /* function available - call it */ | 605 | /* no function available, but known - give output */ |
778 | else if (servermessages[i].funct) | 606 | else { |
779 | servermessages[i].funct (message); | 607 | /* remove continutation mark */ |
780 | /* no function available, but known - give output */ | 608 | if (message[3] == '-') |
781 | else | 609 | message[3] = ' '; |
782 | { | 610 | |
783 | /* remove continutation mark */ | 611 | /* mutate message for display */ |
784 | if (message[3] == '-') | 612 | message[2] = '*'; |
785 | message[3] = ' '; | 613 | /* giveout message by type */ |
786 | 614 | switch (servermessages[i].type) { | |
787 | /* mutate message for display */ | 615 | case SM_IGNORE: |
788 | message[2] = '*'; | 616 | break; |
789 | /* giveout message by type */ | 617 | case SM_INFO: |
790 | switch (servermessages[i].type) | 618 | /* change marker and send as servermessage */ |
791 | { | 619 | message[2] = '#'; |
792 | case SM_IGNORE: | 620 | writecf(FS_SERV, &message[2]); |
793 | break; | 621 | break; |
794 | case SM_INFO: | 622 | case SM_USERINFO: |
795 | /* change marker and send as servermessage */ | 623 | /* keep marker and send as servermessage */ |
796 | message[2] = '#'; | 624 | writecf(FS_SERV, &message[2]); |
797 | writecf (FS_SERV,&message[2]); | 625 | break; |
798 | break; | 626 | case SM_CHANNEL: |
799 | case SM_USERINFO: | 627 | /* keep marker and send as channelmessage */ |
800 | /* keep marker and send as servermessage */ | 628 | writechan(&message[2]); |
801 | writecf (FS_SERV,&message[2]); | 629 | break; |
802 | break; | 630 | case SM_ERROR: |
803 | case SM_CHANNEL: | 631 | /* change marker and send as errormessage */ |
804 | /* keep marker and send as channelmessage */ | 632 | message[2] = '!'; |
805 | writechan (&message[2]); | 633 | writecf(FS_ERR, &message[2]); |
806 | break; | 634 | break; |
807 | case SM_ERROR: | 635 | default: |
808 | /* change marker and send as errormessage */ | 636 | /* fallback: keep marker and send as servermessage */ |
809 | message[2] = '!'; | 637 | writecf(FS_SERV, &message[2]); |
810 | writecf (FS_ERR,&message[2]); | 638 | } |
811 | break; | 639 | } |
812 | default: | 640 | return; |
813 | /* fallback: keep marker and send as servermessage */ | ||
814 | writecf (FS_SERV,&message[2]); | ||
815 | } | ||
816 | } | ||
817 | return; | ||
818 | } | ||
819 | } | ||
820 | /* message not in list, raise errormessage */ | ||
821 | snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_UNKNOWNMSG), message); | ||
822 | writecf (FS_ERR,tmpstr); | ||
823 | } | ||
824 | else | ||
825 | { | ||
826 | /* message neither public, private or server, raise errormessage */ | ||
827 | snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_BOGUSMSG), message); | ||
828 | writecf (FS_ERR,tmpstr); | ||
829 | } | ||
830 | } | ||
831 | |||
832 | /* offset in buffer (for linebreaks at packet borders) */ | ||
833 | static int bufoff = 0; | ||
834 | |||
835 | /* get data from servers filedescriptor */ | ||
836 | void | ||
837 | networkinput (void) | ||
838 | { | ||
839 | int bytes; | ||
840 | char *tmp = NULL; | ||
841 | #define BUFSIZE 4096 | ||
842 | char buf[BUFSIZE]; /* data buffer */ | ||
843 | char *ltmp = buf; | ||
844 | buf[BUFSIZE-1] = '\0'; /* sanity stop */ | ||
845 | |||
846 | /* receive data at offset */ | ||
847 | bytes = BIO_read (server_conn, &buf[bufoff], BUFSIZE-1 - bufoff); | ||
848 | |||
849 | /* no bytes transferred? raise error message, bail out */ | ||
850 | if (bytes < 0) | ||
851 | { | ||
852 | snprintf (tmpstr, TMPSTRSIZE, "Receive fails, %s.", strerror(errno)); | ||
853 | strncpy(errstr,tmpstr,TMPSTRSIZE-2); | ||
854 | errstr[TMPSTRSIZE-2] = '\0'; | ||
855 | strcat(errstr,"\n"); | ||
856 | writecf (FS_ERR,tmpstr); | ||
857 | status = 0; | ||
858 | } | ||
859 | /* end of file from server? */ | ||
860 | else if (bytes == 0) | ||
861 | { | ||
862 | /* inform user, bail out */ | ||
863 | writecf (FS_SERV,"* EOF from server"); | ||
864 | strncpy(errstr,"* EOF from server",TMPSTRSIZE-2); | ||
865 | errstr[TMPSTRSIZE-2] = '\0'; | ||
866 | strcat(errstr,"\n"); | ||
867 | status = 0; | ||
868 | } | ||
869 | else | ||
870 | { | ||
871 | /* terminate message */ | ||
872 | buf[bytes + bufoff] = '\0'; | ||
873 | /* as long as there are lines .. */ | ||
874 | while ((tmp = strchr (ltmp, '\n')) != NULL) | ||
875 | { | ||
876 | /* did the server send CR+LF instead of LF with the last line? */ | ||
877 | if (tmp[-1] == '\r') | ||
878 | tmp[-1] = '\0'; | ||
879 | |||
880 | /* remove newline from previous message, advance pointer of next | ||
881 | * message */ | ||
882 | tmp[0] = '\0'; | ||
883 | tmp++; | ||
884 | |||
885 | /* we have a last message? give away to line handler! */ | ||
886 | if (ltmp[0]) | ||
887 | { | ||
888 | #ifdef DEBUG | ||
889 | /* debugging? log network input! */ | ||
890 | fprintf (stderr, "<| %s\n", ltmp); | ||
891 | #endif | ||
892 | parsemsg (ltmp); | ||
893 | } | ||
894 | |||
895 | /* move line along .. */ | ||
896 | ltmp = tmp; | ||
897 | } | 641 | } |
898 | /* buffer exhausted, move partial line to start of buffer and go on .. */ | ||
899 | bufoff = (bytes+bufoff) - (ltmp-buf); | ||
900 | if (bufoff > 0) | ||
901 | memmove (buf, ltmp, bufoff); | ||
902 | else | ||
903 | bufoff = 0; | ||
904 | } | 642 | } |
905 | } | 643 | /* message not in list, raise errormessage */ |
906 | 644 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_UNKNOWNMSG), message); | |
907 | void | 645 | writecf(FS_ERR, tmpstr); |
908 | networkoutput (char *msg) | 646 | } else { |
909 | { | 647 | /* message neither public, private or server, raise errormessage */ |
910 | #ifdef DEBUG | 648 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_BOGUSMSG), message); |
911 | /* debugging? log network output! */ | 649 | writecf(FS_ERR, tmpstr); |
912 | fprintf (stderr, ">| %s\n", msg); | 650 | } |
913 | #endif | ||
914 | |||
915 | /* send data to server */ | ||
916 | if (BIO_write (server_conn, msg, strlen (msg)) != strlen (msg)) | ||
917 | writecf (FS_ERR,"Message sending fuzzy."); | ||
918 | |||
919 | /* send line termination to server */ | ||
920 | if (BIO_write (server_conn, "\r\n", 2) != 2) | ||
921 | writecf (FS_ERR,"Message sending fuzzy."); | ||
922 | } | 651 | } |
diff --git a/vchat-ssl.c b/vchat-ssl.c deleted file mode 100755 index adef70d..0000000 --- a/vchat-ssl.c +++ /dev/null | |||
@@ -1,475 +0,0 @@ | |||
1 | /* | ||
2 | * vchat-client - alpha version | ||
3 | * vchat-ssl.c - handling of SSL connection and X509 certificate | ||
4 | * verification | ||
5 | * | ||
6 | * Copyright (C) 2007 Thorsten Schroeder <ths@berlin.ccc.de> | ||
7 | * | ||
8 | * This program is free software. It can be redistributed and/or modified, | ||
9 | * provided that this copyright notice is kept intact. This program is | ||
10 | * distributed in the hope that it will be useful, but without any warranty; | ||
11 | * without even the implied warranty of merchantability or fitness for a | ||
12 | * particular purpose. In no event shall the copyright holder be liable for | ||
13 | * any direct, indirect, incidental or special damages arising in any way out | ||
14 | * of the use of this software. | ||
15 | * | ||
16 | */ | ||
17 | |||
18 | #include <stdio.h> | ||
19 | #include <stdlib.h> | ||
20 | #include <string.h> | ||
21 | #include <assert.h> | ||
22 | |||
23 | #include <openssl/err.h> | ||
24 | #include <openssl/ssl.h> | ||
25 | #include <openssl/bio.h> | ||
26 | #include <openssl/evp.h> | ||
27 | #include <openssl/x509.h> | ||
28 | #include <openssl/x509v3.h> | ||
29 | #include <openssl/conf.h> | ||
30 | |||
31 | #include <readline/readline.h> | ||
32 | |||
33 | #include "vchat.h" | ||
34 | #include "vchat-ssl.h" | ||
35 | |||
36 | const char *vchat_ssl_version = "vchat-ssl.c $Id$"; | ||
37 | |||
38 | typedef int (*vc_x509verify_cb_t)(int, X509_STORE_CTX *); | ||
39 | struct vc_x509store_t { | ||
40 | char *cafile; | ||
41 | char *capath; | ||
42 | char *crlfile; | ||
43 | vc_x509verify_cb_t callback; | ||
44 | vc_askpass_cb_t askpass_callback; | ||
45 | STACK_OF(X509) *certs; | ||
46 | STACK_OF(X509_CRL) *crls; | ||
47 | char *use_certfile; | ||
48 | STACK_OF(X509) *use_certs; | ||
49 | char *use_keyfile; | ||
50 | EVP_PKEY *use_key; | ||
51 | int flags; | ||
52 | }; | ||
53 | |||
54 | static void vc_cleanup_x509store(vc_x509store_t *); // Should not be static but is unused | ||
55 | static SSL_CTX * vc_create_sslctx( vc_x509store_t *vc_store ); | ||
56 | static int vc_verify_callback(int, X509_STORE_CTX *); | ||
57 | static X509_STORE * vc_x509store_create(vc_x509store_t *); | ||
58 | static void vc_x509store_clearflags(vc_x509store_t *, int); | ||
59 | static void vc_x509store_setcafile(vc_x509store_t *, char *); | ||
60 | static void vc_x509store_setcapath(vc_x509store_t *, char *); | ||
61 | static void vc_x509store_setcrlfile(vc_x509store_t *, char *); | ||
62 | static void vc_x509store_addcert(vc_x509store_t *, X509 *); | ||
63 | static void vc_x509store_setcb(vc_x509store_t *, vc_x509verify_cb_t); | ||
64 | |||
65 | #define VC_CTX_ERR_EXIT(se, cx) do { \ | ||
66 | snprintf(tmpstr, TMPSTRSIZE, "CREATE CTX: %s", \ | ||
67 | ERR_error_string (ERR_get_error (), NULL)); \ | ||
68 | writecf(FS_ERR, tmpstr); \ | ||
69 | if(se) X509_STORE_free(se); \ | ||
70 | if(cx) SSL_CTX_free(cx); \ | ||
71 | return(0); \ | ||
72 | } while(0) | ||
73 | |||
74 | #define VC_SETCERT_ERR_EXIT(se, cx, err) do { \ | ||
75 | snprintf(tmpstr, TMPSTRSIZE, "CREATE CTX: %s", err); \ | ||
76 | writecf(FS_ERR, tmpstr); \ | ||
77 | if(se) X509_STORE_free(se); \ | ||
78 | if(cx) SSL_CTX_free(cx); \ | ||
79 | return(NULL); \ | ||
80 | } while(0) | ||
81 | |||
82 | static SSL_CTX * vc_create_sslctx( vc_x509store_t *vc_store ) | ||
83 | { | ||
84 | int i = 0; | ||
85 | int n = 0; | ||
86 | int flags = 0; | ||
87 | int r = 0; | ||
88 | SSL_CTX *ctx = NULL; | ||
89 | X509_STORE *store = NULL; | ||
90 | vc_x509verify_cb_t verify_callback = NULL; | ||
91 | |||
92 | /* Explicitly use TLSv1 (or maybe later) */ | ||
93 | if( !(ctx = SSL_CTX_new(SSLv23_client_method())) ) | ||
94 | VC_CTX_ERR_EXIT(store, ctx); | ||
95 | |||
96 | if( !(store = vc_x509store_create(vc_store)) ) | ||
97 | VC_CTX_ERR_EXIT(store, ctx); | ||
98 | |||
99 | SSL_CTX_set_cert_store(ctx, store); | ||
100 | store = NULL; | ||
101 | /* Disable some insecure protocols explicitly */ | ||
102 | SSL_CTX_set_options(ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); | ||
103 | if (getstroption(CF_CIPHERSUITE)) | ||
104 | SSL_CTX_set_cipher_list(ctx, getstroption(CF_CIPHERSUITE)); | ||
105 | else | ||
106 | SSL_CTX_set_cipher_list(ctx, "ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA"); | ||
107 | |||
108 | SSL_CTX_set_verify_depth (ctx, getintoption(CF_VERIFYSSL)); | ||
109 | |||
110 | if( !(verify_callback = vc_store->callback) ) | ||
111 | verify_callback = vc_verify_callback; | ||
112 | |||
113 | if( !(vc_store->flags & VC_X509S_SSL_VERIFY_MASK) ) { | ||
114 | writecf(FS_DBG, tmpstr); | ||
115 | flags = SSL_VERIFY_NONE; | ||
116 | } | ||
117 | else { | ||
118 | if(vc_store->flags & VC_X509S_SSL_VERIFY_PEER) | ||
119 | flags |= SSL_VERIFY_PEER; | ||
120 | if(vc_store->flags & VC_X509S_SSL_VERIFY_FAIL_IF_NO_PEER_CERT) | ||
121 | flags |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; | ||
122 | if(vc_store->flags & VC_X509S_SSL_VERIFY_CLIENT_ONCE) | ||
123 | flags |= SSL_VERIFY_CLIENT_ONCE; | ||
124 | } | ||
125 | |||
126 | SSL_CTX_set_verify(ctx, flags, verify_callback); | ||
127 | |||
128 | if(vc_store->flags & VC_X509S_USE_CERTIFICATE) { | ||
129 | if(vc_store->use_certfile) | ||
130 | SSL_CTX_use_certificate_chain_file(ctx, vc_store->use_certfile); | ||
131 | else { | ||
132 | SSL_CTX_use_certificate(ctx, | ||
133 | sk_X509_value(vc_store->use_certs, 0)); | ||
134 | for(i=0,n=sk_X509_num(vc_store->use_certs); i<n; i++) | ||
135 | SSL_CTX_add_extra_chain_cert(ctx, | ||
136 | sk_X509_value(vc_store->use_certs, i)); | ||
137 | } | ||
138 | |||
139 | SSL_CTX_set_default_passwd_cb(ctx, vc_store->askpass_callback); | ||
140 | |||
141 | if(vc_store->use_keyfile) { | ||
142 | r=SSL_CTX_use_PrivateKey_file(ctx, vc_store->use_keyfile, | ||
143 | SSL_FILETYPE_PEM); | ||
144 | } else if(vc_store->use_key) | ||
145 | r=SSL_CTX_use_PrivateKey(ctx, vc_store->use_key); | ||
146 | |||
147 | if( r!=1 || !SSL_CTX_check_private_key(ctx)) | ||
148 | VC_SETCERT_ERR_EXIT(store, ctx, "Load private key failed"); | ||
149 | } | ||
150 | |||
151 | SSL_CTX_set_app_data(ctx, vc_store); | ||
152 | return(ctx); | ||
153 | } | ||
154 | |||
155 | int vc_connect_ssl( BIO **conn, vc_x509store_t *vc_store ) | ||
156 | { | ||
157 | SSL_CTX * ctx = vc_create_sslctx(vc_store); | ||
158 | X509 *peercert = NULL; | ||
159 | BIO *ssl_conn = NULL; | ||
160 | const SSL *sslp = NULL; | ||
161 | const SSL_CIPHER * cipher = NULL; | ||
162 | |||
163 | /* To display and check server fingerprint */ | ||
164 | char fingerprint[EVP_MAX_MD_SIZE*4]; | ||
165 | unsigned char fingerprint_bin[EVP_MAX_MD_SIZE]; | ||
166 | unsigned int fingerprint_len; | ||
167 | |||
168 | FILE *fingerprint_file = NULL; | ||
169 | char * fp = fingerprint; | ||
170 | |||
171 | long result, j; | ||
172 | |||
173 | if( !ctx ) | ||
174 | return 1; | ||
175 | |||
176 | ssl_conn = BIO_new_ssl(ctx, 1); | ||
177 | SSL_CTX_free(ctx); | ||
178 | |||
179 | if( !ssl_conn ) | ||
180 | goto ssl_error; | ||
181 | |||
182 | BIO_push( ssl_conn, *conn ); | ||
183 | *conn = ssl_conn; | ||
184 | fflush(stdout); | ||
185 | |||
186 | if( BIO_do_handshake( *conn ) <= 0 ) | ||
187 | goto ssl_error; | ||
188 | |||
189 | /* Show information about cipher used */ | ||
190 | /* Get cipher object */ | ||
191 | BIO_get_ssl(ssl_conn, &sslp); | ||
192 | if (sslp) | ||
193 | cipher = SSL_get_current_cipher(sslp); | ||
194 | if (cipher) { | ||
195 | char cipher_desc[TMPSTRSIZE]; | ||
196 | snprintf(tmpstr, TMPSTRSIZE, "[SSL CIPHER ] %s", SSL_CIPHER_description(cipher, cipher_desc, TMPSTRSIZE)); | ||
197 | writecf(FS_SERV, tmpstr); | ||
198 | } else { | ||
199 | snprintf(tmpstr, TMPSTRSIZE, "[SSL ERROR ] Cipher not known / SSL object can't be queried!"); | ||
200 | writecf(FS_ERR, tmpstr); | ||
201 | } | ||
202 | |||
203 | /* Accept being connected, _if_ verification passed */ | ||
204 | if (!sslp) | ||
205 | goto ssl_error; | ||
206 | |||
207 | peercert = SSL_get_peer_certificate(sslp); | ||
208 | if (!peercert) | ||
209 | goto ssl_error; | ||
210 | |||
211 | /* show basic information about peer cert */ | ||
212 | snprintf(tmpstr, TMPSTRSIZE, "[SSL SUBJECT ] %s", X509_NAME_oneline(X509_get_subject_name(peercert),0,0)); | ||
213 | writecf(FS_SERV, tmpstr); | ||
214 | snprintf(tmpstr, TMPSTRSIZE, "[SSL ISSUER ] %s", X509_NAME_oneline(X509_get_issuer_name(peercert),0,0)); | ||
215 | writecf(FS_SERV, tmpstr); | ||
216 | |||
217 | /* calculate fingerprint */ | ||
218 | if (!X509_digest(peercert,EVP_sha1(),fingerprint_bin,&fingerprint_len)) | ||
219 | goto ssl_error; | ||
220 | |||
221 | assert ( ( fingerprint_len > 1 ) && (fingerprint_len <= EVP_MAX_MD_SIZE )); | ||
222 | for (j=0; j<(int)fingerprint_len; j++) | ||
223 | fp += sprintf(fp, "%02X:", fingerprint_bin[j]); | ||
224 | assert ( fp > fingerprint ); | ||
225 | fp[-1] = 0; | ||
226 | snprintf(tmpstr, TMPSTRSIZE, "[SSL FINGERPRINT ] %s (from server)", fingerprint); | ||
227 | writecf(FS_SERV, tmpstr); | ||
228 | |||
229 | /* we don't need the peercert anymore */ | ||
230 | X509_free(peercert); | ||
231 | |||
232 | /* verify fingerprint */ | ||
233 | if (getintoption(CF_PINFINGER)) { | ||
234 | |||
235 | fingerprint_file = fopen(tilde_expand(getstroption(CF_FINGERPRINT)), "r"); | ||
236 | if (fingerprint_file) { | ||
237 | |||
238 | /* Read fingerprint from file */ | ||
239 | char old_fingerprint[EVP_MAX_MD_SIZE*4]; | ||
240 | char * r = fgets(old_fingerprint, sizeof(old_fingerprint), fingerprint_file); | ||
241 | fclose(fingerprint_file); | ||
242 | |||
243 | if (r) { | ||
244 | /* chomp */ | ||
245 | char *nl = strchr(r, '\n'); | ||
246 | if (nl) *nl = 0; | ||
247 | |||
248 | /* verify fingerprint matches stored version */ | ||
249 | if (!strcmp(fingerprint, old_fingerprint)) | ||
250 | return 0; | ||
251 | } | ||
252 | |||
253 | snprintf(tmpstr, TMPSTRSIZE, "[SSL FINGERPRINT ] %s (from %s)", r ? old_fingerprint : "<FILE READ ERROR>", getstroption(CF_FINGERPRINT)); | ||
254 | writecf(FS_ERR, tmpstr); | ||
255 | writecf(FS_ERR, "[SSL CONNECT ERROR] Fingerprint mismatch! Server cert updated?"); | ||
256 | return 1; | ||
257 | } | ||
258 | |||
259 | fingerprint_file = fopen(tilde_expand(getstroption(CF_FINGERPRINT)), "w"); | ||
260 | if (!fingerprint_file) { | ||
261 | snprintf (tmpstr, TMPSTRSIZE, "[WARNING] Can't write fingerprint file, %s.", strerror(errno)); | ||
262 | writecf(FS_ERR, tmpstr); | ||
263 | } else { | ||
264 | fputs(fingerprint, fingerprint_file); | ||
265 | fclose(fingerprint_file); | ||
266 | writecf(FS_SERV, "Stored fingerprint."); | ||
267 | } | ||
268 | return 0; | ||
269 | } | ||
270 | |||
271 | /* If verify of x509 chain was requested, do the check here */ | ||
272 | result = SSL_get_verify_result(sslp); | ||
273 | |||
274 | if (result == X509_V_OK) | ||
275 | return 0; | ||
276 | |||
277 | if (getintoption(CF_IGNSSL)) { | ||
278 | writecf(FS_ERR, "[SSL VERIFY ERROR ] FAILURE IGNORED!!!"); | ||
279 | return 0; | ||
280 | } | ||
281 | |||
282 | ssl_error: | ||
283 | snprintf(tmpstr, TMPSTRSIZE, "[SSL CONNECT ERROR] %s", ERR_error_string (ERR_get_error (), NULL)); | ||
284 | writecf(FS_ERR, tmpstr); | ||
285 | |||
286 | return 1; | ||
287 | } | ||
288 | |||
289 | #define VC_STORE_ERR_EXIT(s) do { \ | ||
290 | fprintf(stderr, "[E] SSL_STORE: %s\n", ERR_error_string (ERR_get_error (), NULL)); \ | ||
291 | if(s) X509_STORE_free(s); \ | ||
292 | return(0); \ | ||
293 | } while(0) | ||
294 | |||
295 | X509_STORE *vc_x509store_create(vc_x509store_t *vc_store) | ||
296 | { | ||
297 | int i = 0; | ||
298 | int n = 0; | ||
299 | X509_STORE *store = NULL; | ||
300 | X509_LOOKUP *lookup = NULL; | ||
301 | |||
302 | store = X509_STORE_new(); | ||
303 | |||
304 | if(vc_store->callback) | ||
305 | X509_STORE_set_verify_cb_func(store, vc_store->callback); | ||
306 | else | ||
307 | X509_STORE_set_verify_cb_func(store, vc_verify_callback); | ||
308 | |||
309 | if( !(lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file())) ) | ||
310 | VC_STORE_ERR_EXIT(store); | ||
311 | |||
312 | if(!vc_store->cafile) { | ||
313 | if( !(vc_store->flags & VC_X509S_NODEF_CAFILE) ) | ||
314 | X509_LOOKUP_load_file(lookup, 0, X509_FILETYPE_DEFAULT); | ||
315 | } else if( !X509_LOOKUP_load_file(lookup, vc_store->cafile, | ||
316 | X509_FILETYPE_PEM) ) | ||
317 | VC_STORE_ERR_EXIT(store); | ||
318 | |||
319 | if(vc_store->crlfile) { | ||
320 | if( !X509_load_crl_file(lookup, vc_store->crlfile, | ||
321 | X509_FILETYPE_PEM) ) | ||
322 | VC_STORE_ERR_EXIT(store); | ||
323 | |||
324 | X509_STORE_set_flags( store, | ||
325 | X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL ); | ||
326 | } | ||
327 | |||
328 | if( !(lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir())) ) | ||
329 | VC_STORE_ERR_EXIT(store); | ||
330 | |||
331 | if( !vc_store->capath ) { | ||
332 | if( !(vc_store->flags & VC_X509S_NODEF_CAPATH) ) | ||
333 | X509_LOOKUP_add_dir(lookup, 0, X509_FILETYPE_DEFAULT); | ||
334 | } else if( !X509_LOOKUP_add_dir(lookup, vc_store->capath, | ||
335 | X509_FILETYPE_PEM) ) | ||
336 | VC_STORE_ERR_EXIT(store); | ||
337 | |||
338 | for( i=0, n=sk_X509_num(vc_store->certs); i<n; i++) | ||
339 | if( !X509_STORE_add_cert(store, sk_X509_value(vc_store->certs, i)) ) | ||
340 | VC_STORE_ERR_EXIT(store); | ||
341 | |||
342 | for( i=0, n=sk_X509_CRL_num(vc_store->crls); i<n; i++) | ||
343 | if( !X509_STORE_add_crl(store, | ||
344 | sk_X509_CRL_value(vc_store->crls, i)) ) | ||
345 | VC_STORE_ERR_EXIT(store); | ||
346 | |||
347 | return(store); | ||
348 | } | ||
349 | |||
350 | int vc_verify_callback(int ok, X509_STORE_CTX *store) | ||
351 | { | ||
352 | if(!ok) { | ||
353 | snprintf(tmpstr, TMPSTRSIZE, "[SSL VERIFY ERROR ] %s", | ||
354 | X509_verify_cert_error_string(X509_STORE_CTX_get_error(store))); | ||
355 | writecf(FS_ERR, tmpstr); | ||
356 | } | ||
357 | return (ok | getintoption(CF_IGNSSL)); | ||
358 | } | ||
359 | |||
360 | void vc_x509store_setflags(vc_x509store_t *store, int flags) | ||
361 | { | ||
362 | store->flags |= flags; | ||
363 | } | ||
364 | |||
365 | void vc_x509store_clearflags(vc_x509store_t *store, int flags) | ||
366 | { | ||
367 | store->flags &= ~flags; | ||
368 | } | ||
369 | |||
370 | void vc_x509store_setcb(vc_x509store_t *store, | ||
371 | vc_x509verify_cb_t callback) | ||
372 | { | ||
373 | store->callback = callback; | ||
374 | } | ||
375 | |||
376 | void vc_x509store_set_pkeycb(vc_x509store_t *store, | ||
377 | vc_askpass_cb_t callback) | ||
378 | { | ||
379 | store->askpass_callback = callback; | ||
380 | } | ||
381 | |||
382 | void vc_x509store_addcert(vc_x509store_t *store, X509 *cert) | ||
383 | { | ||
384 | sk_X509_push(store->certs, cert); | ||
385 | } | ||
386 | |||
387 | void vc_x509store_setcafile(vc_x509store_t *store, char *file) | ||
388 | { | ||
389 | free(store->cafile); | ||
390 | store->cafile = ( file ? strdup(file) : 0 ); | ||
391 | } | ||
392 | |||
393 | void vc_x509store_setcapath(vc_x509store_t *store, char *path) | ||
394 | { | ||
395 | free(store->capath); | ||
396 | store->capath = ( path ? strdup(path) : 0 ); | ||
397 | } | ||
398 | |||
399 | void vc_x509store_setcrlfile(vc_x509store_t *store, char *file) | ||
400 | { | ||
401 | free(store->crlfile); | ||
402 | store->crlfile = ( file ? strdup(file) : 0 ); | ||
403 | } | ||
404 | |||
405 | void vc_x509store_setkeyfile(vc_x509store_t *store, char *file) | ||
406 | { | ||
407 | free(store->use_keyfile); | ||
408 | store->use_keyfile = ( file ? strdup(file) : 0 ); | ||
409 | } | ||
410 | |||
411 | void vc_x509store_setcertfile(vc_x509store_t *store, char *file) | ||
412 | { | ||
413 | free(store->use_certfile); | ||
414 | store->use_certfile = ( file ? strdup(file) : 0 ); | ||
415 | } | ||
416 | |||
417 | #if 0 | ||
418 | int vc_tls_read() | ||
419 | { | ||
420 | |||
421 | } | ||
422 | |||
423 | int vc_tls_read() | ||
424 | { | ||
425 | |||
426 | } | ||
427 | #endif | ||
428 | |||
429 | vc_x509store_t *vc_init_x509store() | ||
430 | { | ||
431 | vc_x509store_t *s = malloc(sizeof(vc_x509store_t)); | ||
432 | if (s) { | ||
433 | |||
434 | static int sslinit; | ||
435 | if( !sslinit++ ) { | ||
436 | SSL_library_init (); | ||
437 | SSL_load_error_strings(); | ||
438 | } | ||
439 | |||
440 | s->cafile = NULL; | ||
441 | s->capath = NULL; | ||
442 | s->crlfile = NULL; | ||
443 | s->callback = NULL; | ||
444 | s->askpass_callback = NULL; | ||
445 | s->certs = sk_X509_new_null(); | ||
446 | s->crls = sk_X509_CRL_new_null(); | ||
447 | s->use_certfile = NULL; | ||
448 | s->use_certs = sk_X509_new_null(); | ||
449 | s->use_keyfile = NULL; | ||
450 | s->use_key = NULL; | ||
451 | s->flags = 0; | ||
452 | } | ||
453 | return s; | ||
454 | } | ||
455 | |||
456 | void vc_cleanup_x509store(vc_x509store_t *s) | ||
457 | { | ||
458 | free(s->cafile); | ||
459 | free(s->capath); | ||
460 | free(s->crlfile); | ||
461 | free(s->use_certfile); | ||
462 | free(s->use_keyfile); | ||
463 | free(s->use_key); | ||
464 | sk_X509_free(s->certs); | ||
465 | sk_X509_CRL_free(s->crls); | ||
466 | sk_X509_free(s->use_certs); | ||
467 | } | ||
468 | |||
469 | const char *vchat_ssl_version_external = "OpenSSL implementation; version unknown"; | ||
470 | void vchat_ssl_get_version_external() | ||
471 | { | ||
472 | char tmpstr[TMPSTRSIZE]; | ||
473 | snprintf(tmpstr, TMPSTRSIZE, "%s with %s", SSLeay_version(SSLEAY_VERSION), SSLeay_version(SSLEAY_CFLAGS)); | ||
474 | vchat_ssl_version_external = strdup(tmpstr); | ||
475 | } | ||
diff --git a/vchat-ssl.h b/vchat-ssl.h deleted file mode 100755 index 8dc1bfc..0000000 --- a/vchat-ssl.h +++ /dev/null | |||
@@ -1,23 +0,0 @@ | |||
1 | |||
2 | /* prototypes */ | ||
3 | |||
4 | struct vc_x509store_t; | ||
5 | typedef struct vc_x509store_t vc_x509store_t; | ||
6 | typedef int (*vc_askpass_cb_t)(char *, int, int, void *); | ||
7 | |||
8 | vc_x509store_t *vc_init_x509store(); | ||
9 | void vc_x509store_set_pkeycb(vc_x509store_t *, vc_askpass_cb_t); | ||
10 | void vc_x509store_setflags(vc_x509store_t *, int); | ||
11 | void vc_x509store_setkeyfile(vc_x509store_t *, char *); | ||
12 | void vc_x509store_setcertfile(vc_x509store_t *, char *); | ||
13 | int vc_connect_ssl(BIO **conn, vc_x509store_t * ); | ||
14 | |||
15 | #define VC_X509S_NODEF_CAFILE 0x01 | ||
16 | #define VC_X509S_NODEF_CAPATH 0x02 | ||
17 | #define VC_X509S_USE_CERTIFICATE 0x04 | ||
18 | #define VC_X509S_SSL_VERIFY_NONE 0x10 | ||
19 | #define VC_X509S_SSL_VERIFY_PEER 0x20 | ||
20 | #define VC_X509S_SSL_VERIFY_FAIL_IF_NO_PEER_CERT 0x40 | ||
21 | #define VC_X509S_SSL_VERIFY_CLIENT_ONCE 0x80 | ||
22 | #define VC_X509S_SSL_VERIFY_MASK 0xF0 | ||
23 | |||
diff --git a/vchat-tls.c b/vchat-tls.c new file mode 100644 index 0000000..eaa12f4 --- /dev/null +++ b/vchat-tls.c | |||
@@ -0,0 +1,970 @@ | |||
1 | /* | ||
2 | * vchat-client - alpha version | ||
3 | * vchat-tls.c - handling of SSL connection and X509 certificate | ||
4 | * verification | ||
5 | * | ||
6 | * Copyright (C) 2007 Thorsten Schroeder <ths@berlin.ccc.de> | ||
7 | * | ||
8 | * This program is free software. It can be redistributed and/or modified, | ||
9 | * provided that this copyright notice is kept intact. This program is | ||
10 | * distributed in the hope that it will be useful, but without any warranty; | ||
11 | * without even the implied warranty of merchantability or fitness for a | ||
12 | * particular purpose. In no event shall the copyright holder be liable for | ||
13 | * any direct, indirect, incidental or special damages arising in any way out | ||
14 | * of the use of this software. | ||
15 | * | ||
16 | */ | ||
17 | |||
18 | #include <assert.h> | ||
19 | #include <errno.h> | ||
20 | #include <stdio.h> | ||
21 | #include <stdlib.h> | ||
22 | #include <string.h> | ||
23 | |||
24 | #include <readline/readline.h> | ||
25 | |||
26 | #include "vchat-tls.h" | ||
27 | #include "vchat.h" | ||
28 | |||
29 | const char *vchat_tls_version = | ||
30 | "vchat-tls.c $Id$"; | ||
31 | |||
32 | /* Helpers to work with vc_x509store_t used by all tls libs */ | ||
33 | void vc_cleanup_x509store(vc_x509store_t *store) { | ||
34 | free(store->cafile); | ||
35 | free(store->capath); | ||
36 | free(store->crlfile); | ||
37 | free(store->certfile); | ||
38 | free(store->keyfile); | ||
39 | memset(store, 0, sizeof(vc_x509store_t)); | ||
40 | } | ||
41 | |||
42 | void vc_x509store_setflags(vc_x509store_t *store, int flags) { | ||
43 | store->flags |= flags; | ||
44 | } | ||
45 | void vc_x509store_clearflags(vc_x509store_t *store, int flags) { | ||
46 | store->flags &= ~flags; | ||
47 | } | ||
48 | void vc_x509store_set_pkeycb(vc_x509store_t *store, vc_askpass_cb_t callback) { | ||
49 | store->askpass_callback = callback; | ||
50 | } | ||
51 | |||
52 | void vc_x509store_setcafile(vc_x509store_t *store, char *file) { | ||
53 | free(store->cafile); | ||
54 | store->cafile = (file ? strdup(file) : 0); | ||
55 | store->flags |= VC_X509S_USE_CAFILE; | ||
56 | } | ||
57 | |||
58 | void vc_x509store_setcapath(vc_x509store_t *store, char *path) { | ||
59 | free(store->capath); | ||
60 | store->capath = (path ? strdup(path) : 0); | ||
61 | } | ||
62 | |||
63 | void vc_x509store_setcrlfile(vc_x509store_t *store, char *file) { | ||
64 | free(store->crlfile); | ||
65 | store->crlfile = (file ? strdup(file) : 0); | ||
66 | } | ||
67 | |||
68 | void vc_x509store_setkeyfile(vc_x509store_t *store, char *file) { | ||
69 | free(store->keyfile); | ||
70 | store->keyfile = (file ? strdup(file) : 0); | ||
71 | } | ||
72 | |||
73 | void vc_x509store_setcertfile(vc_x509store_t *store, char *file) { | ||
74 | free(store->certfile); | ||
75 | store->certfile = (file ? strdup(file) : 0); | ||
76 | store->flags |= VC_X509S_USE_CERTIFICATE; | ||
77 | } | ||
78 | |||
79 | static int verify_or_store_fingerprint(const char *fingerprint) { | ||
80 | char *fingerprint_file_path = tilde_expand(getstroption(CF_FINGERPRINT)); | ||
81 | if (!fingerprint_file_path) { | ||
82 | writecf(FS_ERR, "Error: The CF_FINGERPRINT path is not set but " | ||
83 | "CF_PINFINGER was requested."); | ||
84 | return -1; | ||
85 | } | ||
86 | |||
87 | FILE *fingerprint_file = fopen(fingerprint_file_path, "r"); | ||
88 | if (fingerprint_file) { | ||
89 | /* Read fingerprint from file */ | ||
90 | char old_fingerprint[128]; | ||
91 | char *r = fgets(old_fingerprint, sizeof(old_fingerprint), fingerprint_file); | ||
92 | fclose(fingerprint_file); | ||
93 | |||
94 | if (r) { | ||
95 | /* chomp */ | ||
96 | char *nl = strchr(r, '\n'); | ||
97 | if (nl) | ||
98 | *nl = 0; | ||
99 | |||
100 | /* verify fingerprint matches stored version */ | ||
101 | if (!strcmp(fingerprint, old_fingerprint)) { | ||
102 | writecf(FS_SERV, "[FINGERPRINT MATCH ]"); | ||
103 | goto cleanup_happy; | ||
104 | } | ||
105 | } | ||
106 | |||
107 | snprintf(tmpstr, TMPSTRSIZE, | ||
108 | "Error: Found pinned fingerprint (in %s) %s but expected %s", | ||
109 | r ? old_fingerprint : "<FILE READ ERROR>", | ||
110 | getstroption(CF_FINGERPRINT), fingerprint); | ||
111 | writecf(FS_ERR, tmpstr); | ||
112 | writecf(FS_ERR, "Error: Fingerprint mismatch! Server cert updated?"); | ||
113 | free(fingerprint_file_path); | ||
114 | return 1; | ||
115 | } else | ||
116 | writecf(FS_ERR, | ||
117 | "Warning: No pinned fingerprint found, writing the current one."); | ||
118 | |||
119 | fingerprint_file = fopen(fingerprint_file_path, "w"); | ||
120 | if (!fingerprint_file) { | ||
121 | snprintf(tmpstr, TMPSTRSIZE, "Warning: Can't write fingerprint file, %s.", | ||
122 | strerror(errno)); | ||
123 | writecf(FS_ERR, tmpstr); | ||
124 | } else { | ||
125 | fputs(fingerprint, fingerprint_file); | ||
126 | fclose(fingerprint_file); | ||
127 | writecf(FS_SERV, "[FINGERPRINT STORED ]"); | ||
128 | } | ||
129 | cleanup_happy: | ||
130 | free(fingerprint_file_path); | ||
131 | return 0; | ||
132 | } | ||
133 | |||
134 | #ifdef TLS_LIB_OPENSSL | ||
135 | |||
136 | #include <openssl/bio.h> | ||
137 | #include <openssl/conf.h> | ||
138 | #include <openssl/err.h> | ||
139 | #include <openssl/evp.h> | ||
140 | #include <openssl/ssl.h> | ||
141 | #include <openssl/x509.h> | ||
142 | #include <openssl/x509v3.h> | ||
143 | |||
144 | char *vc_openssl_version() { | ||
145 | snprintf(tmpstr, sizeof(tmpstr), "OpenSSL %s with %s", | ||
146 | SSLeay_version(SSLEAY_VERSION), SSLeay_version(SSLEAY_CFLAGS)); | ||
147 | return strdup(tmpstr); | ||
148 | } | ||
149 | |||
150 | void vc_openssl_init_x509store(vc_x509store_t *store) { | ||
151 | static int sslinit; | ||
152 | if (!sslinit++) { | ||
153 | SSL_library_init(); | ||
154 | SSL_load_error_strings(); | ||
155 | } | ||
156 | memset(store, 0, sizeof(vc_x509store_t)); | ||
157 | |||
158 | /* We want to make verifying the peer the default */ | ||
159 | store->flags |= VC_X509S_SSL_VERIFY_PEER; | ||
160 | } | ||
161 | |||
162 | /* connection BIO for openssl */ | ||
163 | static BIO *server_conn = NULL; | ||
164 | |||
165 | static SSL_CTX *vc_create_sslctx(vc_x509store_t *vc_store); | ||
166 | static int vc_verify_callback(int, X509_STORE_CTX *); | ||
167 | static X509_STORE *vc_x509store_create(vc_x509store_t *); | ||
168 | |||
169 | static SSL_CTX *vc_create_sslctx(vc_x509store_t *vc_store) { | ||
170 | int flags = 0; | ||
171 | |||
172 | /* Explicitly use TLSv1 (or maybe later) */ | ||
173 | SSL_CTX *ctx = SSL_CTX_new(TLS_client_method()); | ||
174 | X509_STORE *store = vc_x509store_create(vc_store); | ||
175 | |||
176 | if (!ctx || !store) { | ||
177 | snprintf(tmpstr, sizeof(tmpstr), "CREATE CTX: %s", | ||
178 | ERR_error_string(ERR_get_error(), NULL)); | ||
179 | writecf(FS_ERR, tmpstr); | ||
180 | if (store) | ||
181 | X509_STORE_free(store); | ||
182 | if (ctx) | ||
183 | SSL_CTX_free(ctx); | ||
184 | return NULL; | ||
185 | } | ||
186 | |||
187 | SSL_CTX_set_cert_store(ctx, store); | ||
188 | |||
189 | /* Disable some insecure protocols explicitly */ | ||
190 | SSL_CTX_set_options(ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); | ||
191 | if (getstroption(CF_CIPHERSUITE)) | ||
192 | SSL_CTX_set_cipher_list(ctx, getstroption(CF_CIPHERSUITE)); | ||
193 | else | ||
194 | SSL_CTX_set_cipher_list(ctx, | ||
195 | "ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA"); | ||
196 | |||
197 | SSL_CTX_set_verify_depth(ctx, getintoption(CF_VERIFYSSL)); | ||
198 | |||
199 | if (!(vc_store->flags & VC_X509S_SSL_VERIFY_MASK)) { | ||
200 | writecf(FS_DBG, tmpstr); | ||
201 | flags = SSL_VERIFY_NONE; | ||
202 | } else { | ||
203 | if (vc_store->flags & VC_X509S_SSL_VERIFY_PEER) | ||
204 | flags |= SSL_VERIFY_PEER; | ||
205 | if (vc_store->flags & VC_X509S_SSL_VERIFY_FAIL_IF_NO_PEER_CERT) | ||
206 | flags |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; | ||
207 | if (vc_store->flags & VC_X509S_SSL_VERIFY_CLIENT_ONCE) | ||
208 | flags |= SSL_VERIFY_CLIENT_ONCE; | ||
209 | } | ||
210 | |||
211 | SSL_CTX_set_verify(ctx, flags, vc_verify_callback); | ||
212 | |||
213 | if (vc_store->flags & VC_X509S_USE_CERTIFICATE) { | ||
214 | int r = 0; | ||
215 | if (vc_store->certfile) | ||
216 | SSL_CTX_use_certificate_chain_file(ctx, vc_store->certfile); | ||
217 | |||
218 | SSL_CTX_set_default_passwd_cb(ctx, vc_store->askpass_callback); | ||
219 | |||
220 | if (vc_store->keyfile) | ||
221 | r = SSL_CTX_use_PrivateKey_file(ctx, vc_store->keyfile, SSL_FILETYPE_PEM); | ||
222 | |||
223 | if (r != 1 || !SSL_CTX_check_private_key(ctx)) { | ||
224 | snprintf(tmpstr, sizeof(tmpstr), "CREATE CTX: Load private key failed"); | ||
225 | writecf(FS_ERR, tmpstr); | ||
226 | SSL_CTX_free(ctx); | ||
227 | return NULL; | ||
228 | } | ||
229 | } | ||
230 | |||
231 | SSL_CTX_set_app_data(ctx, vc_store); | ||
232 | return (ctx); | ||
233 | } | ||
234 | |||
235 | int vc_openssl_connect(int serverfd, vc_x509store_t *vc_store) { | ||
236 | SSL_CTX *ctx = vc_create_sslctx(vc_store); | ||
237 | X509 *peercert = NULL; | ||
238 | BIO *ssl_conn = NULL; | ||
239 | const SSL *sslp = NULL; | ||
240 | const SSL_CIPHER *cipher = NULL; | ||
241 | |||
242 | server_conn = BIO_new_socket(serverfd, 1); | ||
243 | |||
244 | /* To display and check server fingerprint */ | ||
245 | char fingerprint[EVP_MAX_MD_SIZE * 4]; | ||
246 | unsigned char fingerprint_bin[EVP_MAX_MD_SIZE]; | ||
247 | unsigned int fingerprint_len; | ||
248 | |||
249 | char *fp = fingerprint; | ||
250 | |||
251 | long j; | ||
252 | |||
253 | writecf(FS_SERV, "[SOCKET CONNECTED ]"); | ||
254 | writecf(FS_SERV, "[UPGRADING TO TLS ]"); | ||
255 | writecf(FS_SERV, "[TLS ENGINE OPENSSL ]"); | ||
256 | |||
257 | if (!ctx) | ||
258 | goto all_errors; | ||
259 | |||
260 | ssl_conn = BIO_new_ssl(ctx, 1); | ||
261 | SSL_CTX_free(ctx); | ||
262 | |||
263 | if (!ssl_conn) | ||
264 | goto ssl_error; | ||
265 | |||
266 | BIO_push(ssl_conn, server_conn); | ||
267 | server_conn = ssl_conn; | ||
268 | fflush(stdout); | ||
269 | |||
270 | if (BIO_do_handshake(server_conn) <= 0) | ||
271 | goto ssl_error; | ||
272 | |||
273 | /* Show information about cipher used */ | ||
274 | /* Get cipher object */ | ||
275 | BIO_get_ssl(ssl_conn, &sslp); | ||
276 | if (!sslp) | ||
277 | goto ssl_error; | ||
278 | |||
279 | cipher = SSL_get_current_cipher(sslp); | ||
280 | if (cipher) { | ||
281 | char cipher_desc[TMPSTRSIZE]; | ||
282 | snprintf(tmpstr, TMPSTRSIZE, "[SSL CIPHER ] %s", | ||
283 | SSL_CIPHER_description(cipher, cipher_desc, TMPSTRSIZE)); | ||
284 | writecf(FS_SERV, tmpstr); | ||
285 | } else { | ||
286 | snprintf(tmpstr, TMPSTRSIZE, | ||
287 | "[SSL ERROR ] Cipher not known / SSL object can't be " | ||
288 | "queried!"); | ||
289 | writecf(FS_ERR, tmpstr); | ||
290 | } | ||
291 | |||
292 | /* Accept being connected, _if_ verification passed */ | ||
293 | peercert = SSL_get_peer_certificate(sslp); | ||
294 | if (!peercert) | ||
295 | goto ssl_error; | ||
296 | |||
297 | /* show basic information about peer cert */ | ||
298 | snprintf(tmpstr, TMPSTRSIZE, "[SSL SUBJECT ] %s", | ||
299 | X509_NAME_oneline(X509_get_subject_name(peercert), 0, 0)); | ||
300 | writecf(FS_SERV, tmpstr); | ||
301 | snprintf(tmpstr, TMPSTRSIZE, "[SSL ISSUER ] %s", | ||
302 | X509_NAME_oneline(X509_get_issuer_name(peercert), 0, 0)); | ||
303 | writecf(FS_SERV, tmpstr); | ||
304 | |||
305 | /* calculate fingerprint */ | ||
306 | if (!X509_digest(peercert, EVP_sha1(), fingerprint_bin, &fingerprint_len)) | ||
307 | goto ssl_error; | ||
308 | |||
309 | assert((fingerprint_len > 1) && (fingerprint_len <= EVP_MAX_MD_SIZE)); | ||
310 | for (j = 0; j < (int)fingerprint_len; j++) | ||
311 | fp += sprintf(fp, "%02X:", fingerprint_bin[j]); | ||
312 | assert(fp > fingerprint); | ||
313 | fp[-1] = 0; | ||
314 | snprintf(tmpstr, TMPSTRSIZE, "[SSL FINGERPRINT ] %s (from server)", | ||
315 | fingerprint); | ||
316 | writecf(FS_SERV, tmpstr); | ||
317 | |||
318 | /* we don't need the peercert anymore */ | ||
319 | X509_free(peercert); | ||
320 | |||
321 | if (getintoption(CF_PINFINGER) && verify_or_store_fingerprint(fingerprint)) | ||
322 | return 1; | ||
323 | |||
324 | /* If verify of x509 chain was requested, do the check here */ | ||
325 | if (X509_V_OK == SSL_get_verify_result(sslp)) | ||
326 | return 0; | ||
327 | |||
328 | if (getintoption(CF_IGNSSL)) { | ||
329 | writecf(FS_ERR, "[SSL VERIFY ERROR ] FAILURE IGNORED!!!"); | ||
330 | return 0; | ||
331 | } | ||
332 | |||
333 | ssl_error: | ||
334 | snprintf(tmpstr, TMPSTRSIZE, "[SSL CONNECT ERROR ] %s", | ||
335 | ERR_error_string(ERR_get_error(), NULL)); | ||
336 | writecf(FS_ERR, tmpstr); | ||
337 | all_errors: | ||
338 | BIO_free_all(server_conn); | ||
339 | server_conn = NULL; | ||
340 | return 1; | ||
341 | } | ||
342 | |||
343 | #define VC_STORE_ERR_EXIT(s) \ | ||
344 | do { \ | ||
345 | fprintf(stderr, "[E] SSL_STORE: %s\n", \ | ||
346 | ERR_error_string(ERR_get_error(), NULL)); \ | ||
347 | if (s) \ | ||
348 | X509_STORE_free(s); \ | ||
349 | return (0); \ | ||
350 | } while (0) | ||
351 | |||
352 | X509_STORE *vc_x509store_create(vc_x509store_t *vc_store) { | ||
353 | X509_STORE *store = NULL; | ||
354 | X509_LOOKUP *lookup = NULL; | ||
355 | |||
356 | store = X509_STORE_new(); | ||
357 | |||
358 | X509_STORE_set_verify_cb_func(store, vc_verify_callback); | ||
359 | |||
360 | if (!(lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()))) | ||
361 | VC_STORE_ERR_EXIT(store); | ||
362 | |||
363 | if (!vc_store->cafile) { | ||
364 | if (!(vc_store->flags & VC_X509S_USE_CAFILE)) | ||
365 | X509_LOOKUP_load_file(lookup, 0, X509_FILETYPE_DEFAULT); | ||
366 | } else if (!X509_LOOKUP_load_file(lookup, vc_store->cafile, | ||
367 | X509_FILETYPE_PEM)) | ||
368 | VC_STORE_ERR_EXIT(store); | ||
369 | |||
370 | if (vc_store->crlfile) { | ||
371 | if (!X509_load_crl_file(lookup, vc_store->crlfile, X509_FILETYPE_PEM)) | ||
372 | VC_STORE_ERR_EXIT(store); | ||
373 | |||
374 | X509_STORE_set_flags(store, | ||
375 | X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL); | ||
376 | } | ||
377 | |||
378 | if (!(lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir()))) | ||
379 | VC_STORE_ERR_EXIT(store); | ||
380 | |||
381 | if (!vc_store->capath) { | ||
382 | if (!(vc_store->flags & VC_X509S_USE_CAPATH)) | ||
383 | X509_LOOKUP_add_dir(lookup, 0, X509_FILETYPE_DEFAULT); | ||
384 | } else if (!X509_LOOKUP_add_dir(lookup, vc_store->capath, X509_FILETYPE_PEM)) | ||
385 | VC_STORE_ERR_EXIT(store); | ||
386 | |||
387 | return (store); | ||
388 | } | ||
389 | |||
390 | int vc_verify_callback(int ok, X509_STORE_CTX *store) { | ||
391 | if (!ok) { | ||
392 | snprintf(tmpstr, TMPSTRSIZE, "[SSL VERIFY ERROR ] %s", | ||
393 | X509_verify_cert_error_string(X509_STORE_CTX_get_error(store))); | ||
394 | writecf(FS_ERR, tmpstr); | ||
395 | } | ||
396 | return (ok | getintoption(CF_IGNSSL)); | ||
397 | } | ||
398 | |||
399 | ssize_t vc_openssl_sendmessage(const void *buf, size_t size) { | ||
400 | return BIO_write(server_conn, buf, size); | ||
401 | } | ||
402 | |||
403 | ssize_t vc_openssl_receivemessage(void *buf, size_t size) { | ||
404 | ssize_t received = (ssize_t)BIO_read(server_conn, buf, size); | ||
405 | if (received != 0) | ||
406 | return received; | ||
407 | if (BIO_should_retry(server_conn)) | ||
408 | return -2; | ||
409 | return 0; | ||
410 | } | ||
411 | |||
412 | void vc_openssl_cleanup() { | ||
413 | BIO_free_all(server_conn); | ||
414 | server_conn = NULL; | ||
415 | } | ||
416 | #endif | ||
417 | |||
418 | #ifdef TLS_LIB_MBEDTLS | ||
419 | |||
420 | #include "mbedtls/error.h" | ||
421 | #include "mbedtls/version.h" | ||
422 | #include <mbedtls/ctr_drbg.h> | ||
423 | #include <mbedtls/debug.h> | ||
424 | #include <mbedtls/entropy.h> | ||
425 | #include <mbedtls/md.h> | ||
426 | #include <mbedtls/net_sockets.h> | ||
427 | #include <mbedtls/pk.h> | ||
428 | #include <mbedtls/ssl.h> | ||
429 | #include <mbedtls/x509.h> | ||
430 | |||
431 | #include <sys/socket.h> | ||
432 | |||
433 | const char *DRBG_PERS = "mbed TLS vchat client"; | ||
434 | #define MAX_SUITES 512 | ||
435 | typedef struct { | ||
436 | mbedtls_entropy_context _entropy; | ||
437 | mbedtls_ctr_drbg_context _ctr_drbg; | ||
438 | mbedtls_x509_crt _cacert; | ||
439 | mbedtls_x509_crt _cert; | ||
440 | mbedtls_pk_context _key; | ||
441 | mbedtls_ssl_context _ssl; | ||
442 | mbedtls_ssl_config _conf; | ||
443 | int ciphersuits[MAX_SUITES]; | ||
444 | } mbedstate; | ||
445 | static mbedstate _mbedtls_state; | ||
446 | |||
447 | char *vc_mbedtls_version() { | ||
448 | snprintf(tmpstr, sizeof(tmpstr), "%s", MBEDTLS_VERSION_STRING_FULL); | ||
449 | return strdup(tmpstr); | ||
450 | } | ||
451 | |||
452 | static int static_tcp_recv(void *ctx, unsigned char *buf, size_t len) { | ||
453 | return recv((int)(intptr_t)ctx, buf, len, 0); | ||
454 | } | ||
455 | static int static_tcp_send(void *ctx, const unsigned char *buf, size_t len) { | ||
456 | return send((int)(intptr_t)ctx, buf, len, 0); | ||
457 | } | ||
458 | static int map_openssl_suite(char *openssl_name); | ||
459 | void vc_mbedtls_init_x509store(vc_x509store_t *store) { | ||
460 | mbedtls_entropy_init(&_mbedtls_state._entropy); | ||
461 | mbedtls_ctr_drbg_init(&_mbedtls_state._ctr_drbg); | ||
462 | |||
463 | mbedtls_ctr_drbg_seed(&_mbedtls_state._ctr_drbg, mbedtls_entropy_func, | ||
464 | &_mbedtls_state._entropy, | ||
465 | (const unsigned char *)DRBG_PERS, sizeof(DRBG_PERS)); | ||
466 | memset(store, 0, sizeof(vc_x509store_t)); | ||
467 | |||
468 | /* We want to make verifying the peer the default */ | ||
469 | store->flags |= VC_X509S_SSL_VERIFY_PEER; | ||
470 | } | ||
471 | |||
472 | static void vc_tls_report_error(int error, char *message) { | ||
473 | size_t used = snprintf(tmpstr, sizeof(tmpstr), "Error: %s", message); | ||
474 | mbedtls_strerror(error, tmpstr + used, sizeof(tmpstr) - used); | ||
475 | writecf(FS_ERR, tmpstr); | ||
476 | } | ||
477 | |||
478 | int vc_mbedtls_connect(int serverfd, vc_x509store_t *vc_store) { | ||
479 | /* Some aliases for shorter references */ | ||
480 | mbedstate *s = &_mbedtls_state; | ||
481 | mbedtls_ssl_config *conf = &_mbedtls_state._conf; | ||
482 | mbedtls_ssl_context *ssl = &_mbedtls_state._ssl; | ||
483 | int ret, suitecount = 0; | ||
484 | char fingerprint[128], *token; | ||
485 | uint8_t digest[20]; | ||
486 | |||
487 | mbedtls_x509_crt_init(&s->_cacert); | ||
488 | mbedtls_x509_crt_init(&s->_cert); | ||
489 | mbedtls_pk_init(&s->_key); | ||
490 | mbedtls_ssl_init(ssl); | ||
491 | mbedtls_ssl_config_init(conf); | ||
492 | |||
493 | writecf(FS_SERV, "[SOCKET CONNECTED ]"); | ||
494 | writecf(FS_SERV, "[UPGRADING TO TLS ]"); | ||
495 | writecf(FS_SERV, "[TLS ENGINE MBEDTLS ]"); | ||
496 | |||
497 | if ((ret = mbedtls_ssl_config_defaults(conf, MBEDTLS_SSL_IS_CLIENT, | ||
498 | MBEDTLS_SSL_TRANSPORT_STREAM, | ||
499 | MBEDTLS_SSL_PRESET_DEFAULT)) != 0) { | ||
500 | vc_tls_report_error(ret, | ||
501 | "Can not initialise tls parameters, mbedtls reports: "); | ||
502 | return -1; | ||
503 | } | ||
504 | |||
505 | /* TODO: Always verify peer */ | ||
506 | mbedtls_ssl_conf_authmode(conf, getintoption(CF_IGNSSL) | ||
507 | ? MBEDTLS_SSL_VERIFY_OPTIONAL | ||
508 | : MBEDTLS_SSL_VERIFY_REQUIRED); | ||
509 | mbedtls_ssl_conf_rng(conf, mbedtls_ctr_drbg_random, &s->_ctr_drbg); | ||
510 | |||
511 | char *ciphers = getstroption(CF_CIPHERSUITE); | ||
512 | if (!ciphers) | ||
513 | ciphers = "ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA"; | ||
514 | ciphers = strdup(ciphers); | ||
515 | for (token = strtok(ciphers, ":"); token && suitecount < MAX_SUITES - 1; | ||
516 | token = strtok(NULL, ":")) { | ||
517 | int suite = mbedtls_ssl_get_ciphersuite_id(token); | ||
518 | if (!suite) | ||
519 | suite = map_openssl_suite(token); | ||
520 | if (suite) | ||
521 | s->ciphersuits[suitecount++] = suite; | ||
522 | } | ||
523 | s->ciphersuits[suitecount++] = 0; | ||
524 | free(ciphers); | ||
525 | |||
526 | mbedtls_ssl_conf_ciphersuites(conf, s->ciphersuits); | ||
527 | |||
528 | if (vc_store->cafile) { | ||
529 | if ((ret = mbedtls_x509_crt_parse_file(&s->_cacert, vc_store->cafile)) != | ||
530 | 0) { | ||
531 | vc_tls_report_error(ret, "Can not load CA cert, mbedtls reports: "); | ||
532 | return -1; | ||
533 | } | ||
534 | mbedtls_ssl_conf_ca_chain(conf, &s->_cacert, NULL); | ||
535 | writecf(FS_SERV, "[CA CERT LOADED ]"); | ||
536 | } | ||
537 | |||
538 | if (vc_store->flags & VC_X509S_USE_CERTIFICATE) { | ||
539 | char *password = NULL; | ||
540 | char password_buf[1024]; | ||
541 | |||
542 | if ((ret = mbedtls_x509_crt_parse_file(&s->_cert, vc_store->certfile)) != | ||
543 | 0) { | ||
544 | vc_tls_report_error(ret, "Can not load client cert, mbedtls reports: "); | ||
545 | return -1; | ||
546 | } | ||
547 | writecf(FS_SERV, "[CLIENT CERT LOADED ]"); | ||
548 | |||
549 | while (1) { | ||
550 | if ((ret = mbedtls_pk_parse_keyfile(&s->_key, vc_store->keyfile, password | ||
551 | #if MBEDTLS_VERSION_MAJOR >= 3 | ||
552 | , | ||
553 | mbedtls_ctr_drbg_random, &s->_ctr_drbg | ||
554 | #endif | ||
555 | )) == 0) | ||
556 | break; | ||
557 | if (ret != MBEDTLS_ERR_PK_PASSWORD_REQUIRED && | ||
558 | ret != MBEDTLS_ERR_PK_PASSWORD_MISMATCH) { | ||
559 | vc_tls_report_error(ret, "Can not load client key, mbedtls reports: "); | ||
560 | return -1; | ||
561 | } | ||
562 | if (ret == MBEDTLS_ERR_PK_PASSWORD_MISMATCH) | ||
563 | vc_tls_report_error(ret, "Wrong passphrase, mbedtls reports: "); | ||
564 | vc_store->askpass_callback(password_buf, sizeof(password_buf), 0, NULL); | ||
565 | password = password_buf; | ||
566 | } | ||
567 | #if defined(__linux__) || defined(__OpenBSD__) | ||
568 | explicit_bzero(password_buf, sizeof(password_buf)); | ||
569 | #else | ||
570 | memset_s(password_buf, sizeof(password_buf), 0, sizeof(password_buf)); | ||
571 | #endif | ||
572 | writecf(FS_SERV, "[CLIENT KEY LOADED ]"); | ||
573 | |||
574 | #if MBEDTLS_VERSION_MAJOR == 3 && MBEDTLS_VERSION_MINOR == 0 | ||
575 | mbedtls_pk_context *pubkey = &(s->_cert.MBEDTLS_PRIVATE(pk)); | ||
576 | #else | ||
577 | mbedtls_pk_context *pubkey = &(s->_cert.pk); | ||
578 | #endif | ||
579 | |||
580 | if ((ret = mbedtls_pk_check_pair(pubkey, &s->_key | ||
581 | #if MBEDTLS_VERSION_MAJOR >= 3 | ||
582 | , | ||
583 | mbedtls_ctr_drbg_random, &s->_ctr_drbg | ||
584 | #endif | ||
585 | )) != 0) { | ||
586 | vc_tls_report_error(ret, "Cert and key mismatch, mbedtls reports: "); | ||
587 | return 1; | ||
588 | } | ||
589 | |||
590 | if ((ret = mbedtls_ssl_conf_own_cert(conf, &s->_cert, &s->_key)) != 0) { | ||
591 | vc_tls_report_error( | ||
592 | ret, "Setting key and cert to tls session fails, mbedtls reports: "); | ||
593 | return -1; | ||
594 | } | ||
595 | } | ||
596 | |||
597 | /* Config constructed, pass to ssl */ | ||
598 | /* Init ssl and config structs and configure ssl ctx */ | ||
599 | if ((ret = mbedtls_ssl_setup(ssl, conf)) != 0) { | ||
600 | vc_tls_report_error( | ||
601 | ret, "Can not configure parameters on tls context, mbedtls reports: "); | ||
602 | return -1; | ||
603 | } | ||
604 | /* TODO: mbedtls_ssl_set_hostname(&ssl, SERVER_NAME) */ | ||
605 | |||
606 | mbedtls_ssl_set_bio(ssl, (void *)(intptr_t)serverfd, static_tcp_send, | ||
607 | static_tcp_recv, NULL); | ||
608 | |||
609 | while ((ret = mbedtls_ssl_handshake(ssl)) != 0) | ||
610 | if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { | ||
611 | vc_tls_report_error(ret, "TLS handshake failed, mbedtls reports: "); | ||
612 | return -1; | ||
613 | } | ||
614 | |||
615 | writecf(FS_SERV, "[TLS HANDSHAKE DONE ]"); | ||
616 | snprintf(tmpstr, TMPSTRSIZE, "[TLS CIPHER SUITE ] %s", | ||
617 | mbedtls_ssl_get_ciphersuite(ssl)); | ||
618 | writecf(FS_SERV, tmpstr); | ||
619 | |||
620 | const mbedtls_x509_crt *peer_cert = mbedtls_ssl_get_peer_cert(ssl); | ||
621 | mbedtls_x509_crt_info(tmpstr, sizeof(tmpstr), "[TLS PEER INFO ] ", | ||
622 | peer_cert); | ||
623 | |||
624 | for (token = strtok(tmpstr, "\n"); token; token = strtok(NULL, "\n")) | ||
625 | writecf(FS_SERV, token); | ||
626 | |||
627 | #if MBEDTLS_VERSION_MAJOR == 3 && MBEDTLS_VERSION_MINOR == 0 | ||
628 | const uint8_t *const rawcert_buf = | ||
629 | peer_cert->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(p); | ||
630 | size_t rawcert_len = peer_cert->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(len); | ||
631 | #else | ||
632 | const uint8_t *const rawcert_buf = peer_cert->raw.p; | ||
633 | size_t rawcert_len = peer_cert->raw.len; | ||
634 | #endif | ||
635 | const mbedtls_md_info_t *mdinfo = mbedtls_md_info_from_string("SHA1"); | ||
636 | if (mdinfo) { | ||
637 | mbedtls_md(mdinfo, rawcert_buf, rawcert_len, digest); | ||
638 | |||
639 | char *fp = fingerprint; | ||
640 | for (int j = 0; j < mbedtls_md_get_size(mdinfo); j++) | ||
641 | fp += sprintf(fp, "%02X:", digest[j]); | ||
642 | assert(fp > fingerprint); | ||
643 | fp[-1] = 0; | ||
644 | snprintf(tmpstr, TMPSTRSIZE, "[TLS FINGERPRINT ] %s (from server)", | ||
645 | fingerprint); | ||
646 | writecf(FS_SERV, tmpstr); | ||
647 | |||
648 | if (getintoption(CF_PINFINGER) && verify_or_store_fingerprint(fingerprint)) | ||
649 | return 1; | ||
650 | } else { | ||
651 | writecf(FS_ERR, "Warning: Unable to load SHA-1 md"); | ||
652 | if (getintoption(CF_PINFINGER)) { | ||
653 | writecf( | ||
654 | FS_ERR, | ||
655 | "ERROR: Can not compute fingerprint, but pinning check is required"); | ||
656 | return 1; | ||
657 | } | ||
658 | } | ||
659 | |||
660 | ret = mbedtls_ssl_get_verify_result(ssl); | ||
661 | switch (ret) { | ||
662 | case 0: | ||
663 | writecf(FS_SERV, "[TLS HANDSHAKE OK ]"); | ||
664 | break; | ||
665 | case -1: | ||
666 | writecf(FS_ERR, "Error: TLS verify for an unknown reason"); | ||
667 | return -1; | ||
668 | case MBEDTLS_X509_BADCERT_SKIP_VERIFY: | ||
669 | case MBEDTLS_X509_BADCERT_NOT_TRUSTED: | ||
670 | if (getintoption(CF_IGNSSL) || !getintoption(CF_VERIFYSSL)) | ||
671 | return 0; | ||
672 | vc_tls_report_error(ret, "TLS verify failed, mbedtls reports: "); | ||
673 | return -1; | ||
674 | default: | ||
675 | vc_tls_report_error(ret, "TLS verify failed, mbedtls reports: "); | ||
676 | return -1; | ||
677 | } | ||
678 | |||
679 | return 0; | ||
680 | } | ||
681 | |||
682 | ssize_t vc_mbedtls_sendmessage(const void *buf, size_t size) { | ||
683 | return mbedtls_ssl_write(&_mbedtls_state._ssl, buf, size); | ||
684 | } | ||
685 | |||
686 | ssize_t vc_mbedtls_receivemessage(void *buf, size_t size) { | ||
687 | ssize_t received = (ssize_t)mbedtls_ssl_read(&_mbedtls_state._ssl, buf, size); | ||
688 | switch (received) { | ||
689 | case MBEDTLS_ERR_SSL_WANT_READ: | ||
690 | case MBEDTLS_ERR_SSL_WANT_WRITE: | ||
691 | return -2; | ||
692 | case MBEDTLS_ERR_SSL_CONN_EOF: | ||
693 | case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY: | ||
694 | case 0: | ||
695 | return 0; | ||
696 | default: | ||
697 | if (received > 0) | ||
698 | return received; | ||
699 | return -1; | ||
700 | } | ||
701 | } | ||
702 | |||
703 | void vc_mbedtls_cleanup() { | ||
704 | mbedtls_x509_crt_free(&_mbedtls_state._cacert); | ||
705 | mbedtls_x509_crt_free(&_mbedtls_state._cert); | ||
706 | mbedtls_pk_free(&_mbedtls_state._key); | ||
707 | mbedtls_ssl_free(&_mbedtls_state._ssl); | ||
708 | mbedtls_ssl_config_free(&_mbedtls_state._conf); | ||
709 | mbedtls_ctr_drbg_free(&_mbedtls_state._ctr_drbg); | ||
710 | } | ||
711 | |||
712 | /* Taken from https://testssl.sh/openssl-iana.mapping.html */ | ||
713 | static const char * xlate_openssl[] = { | ||
714 | "NULL-MD5", "TLS-RSA-WITH-NULL-MD5", | ||
715 | "NULL-SHA", "TLS-RSA-WITH-NULL-SHA", | ||
716 | "EXP-RC4-MD5", "TLS-RSA-EXPORT-WITH-RC4-40-MD5", | ||
717 | "RC4-MD5", "TLS-RSA-WITH-RC4-128-MD5", | ||
718 | "RC4-SHA", "TLS-RSA-WITH-RC4-128-SHA", | ||
719 | "EXP-RC2-CBC-MD5", "TLS-RSA-EXPORT-WITH-RC2-CBC-40-MD5", | ||
720 | "IDEA-CBC-SHA", "TLS-RSA-WITH-IDEA-CBC-SHA", | ||
721 | "EXP-DES-CBC-SHA", "TLS-RSA-EXPORT-WITH-DES40-CBC-SHA", | ||
722 | "DES-CBC-SHA", "TLS-RSA-WITH-DES-CBC-SHA", | ||
723 | "DES-CBC3-SHA", "TLS-RSA-WITH-3DES-EDE-CBC-SHA", | ||
724 | "EXP-DH-DSS-DES-CBC-SHA", "TLS-DH-DSS-EXPORT-WITH-DES40-CBC-SHA", | ||
725 | "DH-DSS-DES-CBC-SHA", "TLS-DH-DSS-WITH-DES-CBC-SHA", | ||
726 | "DH-DSS-DES-CBC3-SHA", "TLS-DH-DSS-WITH-3DES-EDE-CBC-SHA", | ||
727 | "EXP-DH-RSA-DES-CBC-SHA", "TLS-DH-RSA-EXPORT-WITH-DES40-CBC-SHA", | ||
728 | "DH-RSA-DES-CBC-SHA", "TLS-DH-RSA-WITH-DES-CBC-SHA", | ||
729 | "DH-RSA-DES-CBC3-SHA", "TLS-DH-RSA-WITH-3DES-EDE-CBC-SHA", | ||
730 | "EXP-EDH-DSS-DES-CBC-SHA", "TLS-DHE-DSS-EXPORT-WITH-DES40-CBC-SHA", | ||
731 | "EDH-DSS-DES-CBC-SHA", "TLS-DHE-DSS-WITH-DES-CBC-SHA", | ||
732 | "EDH-DSS-DES-CBC3-SHA", "TLS-DHE-DSS-WITH-3DES-EDE-CBC-SHA", | ||
733 | "EXP-EDH-RSA-DES-CBC-SHA", "TLS-DHE-RSA-EXPORT-WITH-DES40-CBC-SHA", | ||
734 | "EDH-RSA-DES-CBC-SHA", "TLS-DHE-RSA-WITH-DES-CBC-SHA", | ||
735 | "EDH-RSA-DES-CBC3-SHA", "TLS-DHE-RSA-WITH-3DES-EDE-CBC-SHA", | ||
736 | "EXP-ADH-RC4-MD5", "TLS-DH-anon-EXPORT-WITH-RC4-40-MD5", | ||
737 | "ADH-RC4-MD5", "TLS-DH-anon-WITH-RC4-128-MD5", | ||
738 | "EXP-ADH-DES-CBC-SHA", "TLS-DH-anon-EXPORT-WITH-DES40-CBC-SHA", | ||
739 | "ADH-DES-CBC-SHA", "TLS-DH-anon-WITH-DES-CBC-SHA", | ||
740 | "ADH-DES-CBC3-SHA", "TLS-DH-anon-WITH-3DES-EDE-CBC-SHA", | ||
741 | "KRB5-DES-CBC-SHA", "TLS-KRB5-WITH-DES-CBC-SHA", | ||
742 | "KRB5-DES-CBC3-SHA", "TLS-KRB5-WITH-3DES-EDE-CBC-SHA", | ||
743 | "KRB5-RC4-SHA", "TLS-KRB5-WITH-RC4-128-SHA", | ||
744 | "KRB5-IDEA-CBC-SHA", "TLS-KRB5-WITH-IDEA-CBC-SHA", | ||
745 | "KRB5-DES-CBC-MD5", "TLS-KRB5-WITH-DES-CBC-MD5", | ||
746 | "KRB5-DES-CBC3-MD5", "TLS-KRB5-WITH-3DES-EDE-CBC-MD5", | ||
747 | "KRB5-RC4-MD5", "TLS-KRB5-WITH-RC4-128-MD5", | ||
748 | "KRB5-IDEA-CBC-MD5", "TLS-KRB5-WITH-IDEA-CBC-MD5", | ||
749 | "EXP-KRB5-DES-CBC-SHA", "TLS-KRB5-EXPORT-WITH-DES-CBC-40-SHA", | ||
750 | "EXP-KRB5-RC2-CBC-SHA", "TLS-KRB5-EXPORT-WITH-RC2-CBC-40-SHA", | ||
751 | "EXP-KRB5-RC4-SHA", "TLS-KRB5-EXPORT-WITH-RC4-40-SHA", | ||
752 | "EXP-KRB5-DES-CBC-MD5", "TLS-KRB5-EXPORT-WITH-DES-CBC-40-MD5", | ||
753 | "EXP-KRB5-RC2-CBC-MD5", "TLS-KRB5-EXPORT-WITH-RC2-CBC-40-MD5", | ||
754 | "EXP-KRB5-RC4-MD5", "TLS-KRB5-EXPORT-WITH-RC4-40-MD5", | ||
755 | "PSK-NULL-SHA", "TLS-PSK-WITH-NULL-SHA", | ||
756 | "DHE-PSK-NULL-SHA", "TLS-DHE-PSK-WITH-NULL-SHA", | ||
757 | "RSA-PSK-NULL-SHA", "TLS-RSA-PSK-WITH-NULL-SHA", | ||
758 | "AES128-SHA", "TLS-RSA-WITH-AES-128-CBC-SHA", | ||
759 | "DH-DSS-AES128-SHA", "TLS-DH-DSS-WITH-AES-128-CBC-SHA", | ||
760 | "DH-RSA-AES128-SHA", "TLS-DH-RSA-WITH-AES-128-CBC-SHA", | ||
761 | "DHE-DSS-AES128-SHA", "TLS-DHE-DSS-WITH-AES-128-CBC-SHA", | ||
762 | "DHE-RSA-AES128-SHA", "TLS-DHE-RSA-WITH-AES-128-CBC-SHA", | ||
763 | "ADH-AES128-SHA", "TLS-DH-anon-WITH-AES-128-CBC-SHA", | ||
764 | "AES256-SHA", "TLS-RSA-WITH-AES-256-CBC-SHA", | ||
765 | "DH-DSS-AES256-SHA", "TLS-DH-DSS-WITH-AES-256-CBC-SHA", | ||
766 | "DH-RSA-AES256-SHA", "TLS-DH-RSA-WITH-AES-256-CBC-SHA", | ||
767 | "DHE-DSS-AES256-SHA", "TLS-DHE-DSS-WITH-AES-256-CBC-SHA", | ||
768 | "DHE-RSA-AES256-SHA", "TLS-DHE-RSA-WITH-AES-256-CBC-SHA", | ||
769 | "ADH-AES256-SHA", "TLS-DH-anon-WITH-AES-256-CBC-SHA", | ||
770 | "NULL-SHA256", "TLS-RSA-WITH-NULL-SHA256", | ||
771 | "AES128-SHA256", "TLS-RSA-WITH-AES-128-CBC-SHA256", | ||
772 | "AES256-SHA256", "TLS-RSA-WITH-AES-256-CBC-SHA256", | ||
773 | "DH-DSS-AES128-SHA256", "TLS-DH-DSS-WITH-AES-128-CBC-SHA256", | ||
774 | "DH-RSA-AES128-SHA256", "TLS-DH-RSA-WITH-AES-128-CBC-SHA256", | ||
775 | "DHE-DSS-AES128-SHA256", "TLS-DHE-DSS-WITH-AES-128-CBC-SHA256", | ||
776 | "CAMELLIA128-SHA", "TLS-RSA-WITH-CAMELLIA-128-CBC-SHA", | ||
777 | "DH-DSS-CAMELLIA128-SHA", "TLS-DH-DSS-WITH-CAMELLIA-128-CBC-SHA", | ||
778 | "DH-RSA-CAMELLIA128-SHA", "TLS-DH-RSA-WITH-CAMELLIA-128-CBC-SHA", | ||
779 | "DHE-DSS-CAMELLIA128-SHA", "TLS-DHE-DSS-WITH-CAMELLIA-128-CBC-SHA", | ||
780 | "DHE-RSA-CAMELLIA128-SHA", "TLS-DHE-RSA-WITH-CAMELLIA-128-CBC-SHA", | ||
781 | "ADH-CAMELLIA128-SHA", "TLS-DH-anon-WITH-CAMELLIA-128-CBC-SHA", | ||
782 | "EXP1024-RC4-MD5", "TLS-RSA-EXPORT1024-WITH-RC4-56-MD5", | ||
783 | "EXP1024-RC2-CBC-MD5", "TLS-RSA-EXPORT1024-WITH-RC2-CBC-56-MD5", | ||
784 | "EXP1024-DES-CBC-SHA", "TLS-RSA-EXPORT1024-WITH-DES-CBC-SHA", | ||
785 | "EXP1024-DHE-DSS-DES-CBC-SHA", "TLS-DHE-DSS-EXPORT1024-WITH-DES-CBC-SHA", | ||
786 | "EXP1024-RC4-SHA", "TLS-RSA-EXPORT1024-WITH-RC4-56-SHA", | ||
787 | "EXP1024-DHE-DSS-RC4-SHA", "TLS-DHE-DSS-EXPORT1024-WITH-RC4-56-SHA", | ||
788 | "DHE-DSS-RC4-SHA", "TLS-DHE-DSS-WITH-RC4-128-SHA", | ||
789 | "DHE-RSA-AES128-SHA256", "TLS-DHE-RSA-WITH-AES-128-CBC-SHA256", | ||
790 | "DH-DSS-AES256-SHA256", "TLS-DH-DSS-WITH-AES-256-CBC-SHA256", | ||
791 | "DH-RSA-AES256-SHA256", "TLS-DH-RSA-WITH-AES-256-CBC-SHA256", | ||
792 | "DHE-DSS-AES256-SHA256", "TLS-DHE-DSS-WITH-AES-256-CBC-SHA256", | ||
793 | "DHE-RSA-AES256-SHA256", "TLS-DHE-RSA-WITH-AES-256-CBC-SHA256", | ||
794 | "ADH-AES128-SHA256", "TLS-DH-anon-WITH-AES-128-CBC-SHA256", | ||
795 | "ADH-AES256-SHA256", "TLS-DH-anon-WITH-AES-256-CBC-SHA256", | ||
796 | "GOST94-GOST89-GOST89", "TLS-GOSTR341094-WITH-28147-CNT-IMIT", | ||
797 | "GOST2001-GOST89-GOST89", "TLS-GOSTR341001-WITH-28147-CNT-IMIT", | ||
798 | "GOST94-NULL-GOST94", "TLS-GOSTR341001-WITH-NULL-GOSTR3411", | ||
799 | "GOST2001-GOST89-GOST89", "TLS-GOSTR341094-WITH-NULL-GOSTR3411", | ||
800 | "CAMELLIA256-SHA", "TLS-RSA-WITH-CAMELLIA-256-CBC-SHA", | ||
801 | "DH-DSS-CAMELLIA256-SHA", "TLS-DH-DSS-WITH-CAMELLIA-256-CBC-SHA", | ||
802 | "DH-RSA-CAMELLIA256-SHA", "TLS-DH-RSA-WITH-CAMELLIA-256-CBC-SHA", | ||
803 | "DHE-DSS-CAMELLIA256-SHA", "TLS-DHE-DSS-WITH-CAMELLIA-256-CBC-SHA", | ||
804 | "DHE-RSA-CAMELLIA256-SHA", "TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA", | ||
805 | "ADH-CAMELLIA256-SHA", "TLS-DH-anon-WITH-CAMELLIA-256-CBC-SHA", | ||
806 | "PSK-RC4-SHA", "TLS-PSK-WITH-RC4-128-SHA", | ||
807 | "PSK-3DES-EDE-CBC-SHA", "TLS-PSK-WITH-3DES-EDE-CBC-SHA", | ||
808 | "PSK-AES128-CBC-SHA", "TLS-PSK-WITH-AES-128-CBC-SHA", | ||
809 | "PSK-AES256-CBC-SHA", "TLS-PSK-WITH-AES-256-CBC-SHA", | ||
810 | "SEED-SHA", "TLS-RSA-WITH-SEED-CBC-SHA", | ||
811 | "DH-DSS-SEED-SHA", "TLS-DH-DSS-WITH-SEED-CBC-SHA", | ||
812 | "DH-RSA-SEED-SHA", "TLS-DH-RSA-WITH-SEED-CBC-SHA", | ||
813 | "DHE-DSS-SEED-SHA", "TLS-DHE-DSS-WITH-SEED-CBC-SHA", | ||
814 | "DHE-RSA-SEED-SHA", "TLS-DHE-RSA-WITH-SEED-CBC-SHA", | ||
815 | "ADH-SEED-SHA", "TLS-DH-anon-WITH-SEED-CBC-SHA", | ||
816 | "AES128-GCM-SHA256", "TLS-RSA-WITH-AES-128-GCM-SHA256", | ||
817 | "AES256-GCM-SHA384", "TLS-RSA-WITH-AES-256-GCM-SHA384", | ||
818 | "DHE-RSA-AES128-GCM-SHA256", "TLS-DHE-RSA-WITH-AES-128-GCM-SHA256", | ||
819 | "DHE-RSA-AES256-GCM-SHA384", "TLS-DHE-RSA-WITH-AES-256-GCM-SHA384", | ||
820 | "DH-RSA-AES128-GCM-SHA256", "TLS-DH-RSA-WITH-AES-128-GCM-SHA256", | ||
821 | "DH-RSA-AES256-GCM-SHA384", "TLS-DH-RSA-WITH-AES-256-GCM-SHA384", | ||
822 | "DHE-DSS-AES128-GCM-SHA256", "TLS-DHE-DSS-WITH-AES-128-GCM-SHA256", | ||
823 | "DHE-DSS-AES256-GCM-SHA384", "TLS-DHE-DSS-WITH-AES-256-GCM-SHA384", | ||
824 | "DH-DSS-AES128-GCM-SHA256", "TLS-DH-DSS-WITH-AES-128-GCM-SHA256", | ||
825 | "DH-DSS-AES256-GCM-SHA384", "TLS-DH-DSS-WITH-AES-256-GCM-SHA384", | ||
826 | "ADH-AES128-GCM-SHA256", "TLS-DH-anon-WITH-AES-128-GCM-SHA256", | ||
827 | "ADH-AES256-GCM-SHA384", "TLS-DH-anon-WITH-AES-256-GCM-SHA384", | ||
828 | "CAMELLIA128-SHA256", "TLS-RSA-WITH-CAMELLIA-128-CBC-SHA256", | ||
829 | "DH-DSS-CAMELLIA128-SHA256", "TLS-DH-DSS-WITH-CAMELLIA-128-CBC-SHA256", | ||
830 | "DH-RSA-CAMELLIA128-SHA256", "TLS-DH-RSA-WITH-CAMELLIA-128-CBC-SHA256", | ||
831 | "DHE-DSS-CAMELLIA128-SHA256", "TLS-DHE-DSS-WITH-CAMELLIA-128-CBC-SHA256", | ||
832 | "DHE-RSA-CAMELLIA128-SHA256", "TLS-DHE-RSA-WITH-CAMELLIA-128-CBC-SHA256", | ||
833 | "ADH-CAMELLIA128-SHA256", "TLS-DH-anon-WITH-CAMELLIA-128-CBC-SHA256", | ||
834 | "TLS-FALLBACK-SCSV", "TLS-EMPTY-RENEGOTIATION-INFO-SCSV", | ||
835 | "TLS-AES-128-GCM-SHA256", "TLS-AES-128-GCM-SHA256", | ||
836 | "TLS-AES-256-GCM-SHA384", "TLS-AES-256-GCM-SHA384", | ||
837 | "TLS-CHACHA20-POLY1305-SHA256", "TLS-CHACHA20-POLY1305-SHA256", | ||
838 | "TLS-AES-128-CCM-SHA256", "TLS-AES-128-CCM-SHA256", | ||
839 | "TLS-AES-128-CCM-8-SHA256", "TLS-AES-128-CCM-8-SHA256", | ||
840 | "ECDH-ECDSA-NULL-SHA", "TLS-ECDH-ECDSA-WITH-NULL-SHA", | ||
841 | "ECDH-ECDSA-RC4-SHA", "TLS-ECDH-ECDSA-WITH-RC4-128-SHA", | ||
842 | "ECDH-ECDSA-DES-CBC3-SHA", "TLS-ECDH-ECDSA-WITH-3DES-EDE-CBC-SHA", | ||
843 | "ECDH-ECDSA-AES128-SHA", "TLS-ECDH-ECDSA-WITH-AES-128-CBC-SHA", | ||
844 | "ECDH-ECDSA-AES256-SHA", "TLS-ECDH-ECDSA-WITH-AES-256-CBC-SHA", | ||
845 | "ECDHE-ECDSA-NULL-SHA", "TLS-ECDHE-ECDSA-WITH-NULL-SHA", | ||
846 | "ECDHE-ECDSA-RC4-SHA", "TLS-ECDHE-ECDSA-WITH-RC4-128-SHA", | ||
847 | "ECDHE-ECDSA-DES-CBC3-SHA", "TLS-ECDHE-ECDSA-WITH-3DES-EDE-CBC-SHA", | ||
848 | "ECDHE-ECDSA-AES128-SHA", "TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA", | ||
849 | "ECDHE-ECDSA-AES256-SHA", "TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA", | ||
850 | "ECDH-RSA-NULL-SHA", "TLS-ECDH-RSA-WITH-NULL-SHA", | ||
851 | "ECDH-RSA-RC4-SHA", "TLS-ECDH-RSA-WITH-RC4-128-SHA", | ||
852 | "ECDH-RSA-DES-CBC3-SHA", "TLS-ECDH-RSA-WITH-3DES-EDE-CBC-SHA", | ||
853 | "ECDH-RSA-AES128-SHA", "TLS-ECDH-RSA-WITH-AES-128-CBC-SHA", | ||
854 | "ECDH-RSA-AES256-SHA", "TLS-ECDH-RSA-WITH-AES-256-CBC-SHA", | ||
855 | "ECDHE-RSA-NULL-SHA", "TLS-ECDHE-RSA-WITH-NULL-SHA", | ||
856 | "ECDHE-RSA-RC4-SHA", "TLS-ECDHE-RSA-WITH-RC4-128-SHA", | ||
857 | "ECDHE-RSA-DES-CBC3-SHA", "TLS-ECDHE-RSA-WITH-3DES-EDE-CBC-SHA", | ||
858 | "ECDHE-RSA-AES128-SHA", "TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA", | ||
859 | "ECDHE-RSA-AES256-SHA", "TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA", | ||
860 | "AECDH-NULL-SHA", "TLS-ECDH-anon-WITH-NULL-SHA", | ||
861 | "AECDH-RC4-SHA", "TLS-ECDH-anon-WITH-RC4-128-SHA", | ||
862 | "AECDH-DES-CBC3-SHA", "TLS-ECDH-anon-WITH-3DES-EDE-CBC-SHA", | ||
863 | "AECDH-AES128-SHA", "TLS-ECDH-anon-WITH-AES-128-CBC-SHA", | ||
864 | "AECDH-AES256-SHA", "TLS-ECDH-anon-WITH-AES-256-CBC-SHA", | ||
865 | "SRP-3DES-EDE-CBC-SHA", "TLS-SRP-SHA-WITH-3DES-EDE-CBC-SHA", | ||
866 | "SRP-RSA-3DES-EDE-CBC-SHA", "TLS-SRP-SHA-RSA-WITH-3DES-EDE-CBC-SHA", | ||
867 | "SRP-DSS-3DES-EDE-CBC-SHA", "TLS-SRP-SHA-DSS-WITH-3DES-EDE-CBC-SHA", | ||
868 | "SRP-AES-128-CBC-SHA", "TLS-SRP-SHA-WITH-AES-128-CBC-SHA", | ||
869 | "SRP-RSA-AES-128-CBC-SHA", "TLS-SRP-SHA-RSA-WITH-AES-128-CBC-SHA", | ||
870 | "SRP-DSS-AES-128-CBC-SHA", "TLS-SRP-SHA-DSS-WITH-AES-128-CBC-SHA", | ||
871 | "SRP-AES-256-CBC-SHA", "TLS-SRP-SHA-WITH-AES-256-CBC-SHA", | ||
872 | "SRP-RSA-AES-256-CBC-SHA", "TLS-SRP-SHA-RSA-WITH-AES-256-CBC-SHA", | ||
873 | "SRP-DSS-AES-256-CBC-SHA", "TLS-SRP-SHA-DSS-WITH-AES-256-CBC-SHA", | ||
874 | "ECDHE-ECDSA-AES128-SHA256", "TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA256", | ||
875 | "ECDHE-ECDSA-AES256-SHA384", "TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA384", | ||
876 | "ECDH-ECDSA-AES128-SHA256", "TLS-ECDH-ECDSA-WITH-AES-128-CBC-SHA256", | ||
877 | "ECDH-ECDSA-AES256-SHA384", "TLS-ECDH-ECDSA-WITH-AES-256-CBC-SHA384", | ||
878 | "ECDHE-RSA-AES128-SHA256", "TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA256", | ||
879 | "ECDHE-RSA-AES256-SHA384", "TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA384", | ||
880 | "ECDH-RSA-AES128-SHA256", "TLS-ECDH-RSA-WITH-AES-128-CBC-SHA256", | ||
881 | "ECDH-RSA-AES256-SHA384", "TLS-ECDH-RSA-WITH-AES-256-CBC-SHA384", | ||
882 | "ECDHE-ECDSA-AES128-GCM-SHA256", "TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256", | ||
883 | "ECDHE-ECDSA-AES256-GCM-SHA384", "TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384", | ||
884 | "ECDH-ECDSA-AES128-GCM-SHA256", "TLS-ECDH-ECDSA-WITH-AES-128-GCM-SHA256", | ||
885 | "ECDH-ECDSA-AES256-GCM-SHA384", "TLS-ECDH-ECDSA-WITH-AES-256-GCM-SHA384", | ||
886 | "ECDHE-RSA-AES128-GCM-SHA256", "TLS-ECDHE-RSA-WITH-AES-128-GCM-SHA256", | ||
887 | "ECDHE-RSA-AES256-GCM-SHA384", "TLS-ECDHE-RSA-WITH-AES-256-GCM-SHA384", | ||
888 | "ECDH-RSA-AES128-GCM-SHA256", "TLS-ECDH-RSA-WITH-AES-128-GCM-SHA256", | ||
889 | "ECDH-RSA-AES256-GCM-SHA384", "TLS-ECDH-RSA-WITH-AES-256-GCM-SHA384", | ||
890 | "ECDHE-PSK-RC4-SHA", "TLS-ECDHE-PSK-WITH-RC4-128-SHA", | ||
891 | "ECDHE-PSK-3DES-EDE-CBC-SHA", "TLS-ECDHE-PSK-WITH-3DES-EDE-CBC-SHA", | ||
892 | "ECDHE-PSK-AES128-CBC-SHA", "TLS-ECDHE-PSK-WITH-AES-128-CBC-SHA", | ||
893 | "ECDHE-PSK-AES256-CBC-SHA", "TLS-ECDHE-PSK-WITH-AES-256-CBC-SHA", | ||
894 | "ECDHE-PSK-AES128-CBC-SHA256", "TLS-ECDHE-PSK-WITH-AES-128-CBC-SHA256", | ||
895 | "ECDHE-PSK-AES256-CBC-SHA384", "TLS-ECDHE-PSK-WITH-AES-256-CBC-SHA384", | ||
896 | "ECDHE-PSK-NULL-SHA", "TLS-ECDHE-PSK-WITH-NULL-SHA", | ||
897 | "ECDHE-PSK-NULL-SHA256", "TLS-ECDHE-PSK-WITH-NULL-SHA256", | ||
898 | "ECDHE-PSK-NULL-SHA384", "TLS-ECDHE-PSK-WITH-NULL-SHA384", | ||
899 | "ECDHE-ECDSA-CAMELLIA128-SHA256", "TLS-ECDHE-ECDSA-WITH-CAMELLIA-128-CBC-SHA256", | ||
900 | "ECDHE-ECDSA-CAMELLIA256-SHA38", "TLS-ECDHE-ECDSA-WITH-CAMELLIA-256-CBC-SHA384", | ||
901 | "ECDH-ECDSA-CAMELLIA128-SHA256", "TLS-ECDH-ECDSA-WITH-CAMELLIA-128-CBC-SHA256", | ||
902 | "ECDH-ECDSA-CAMELLIA256-SHA384", "TLS-ECDH-ECDSA-WITH-CAMELLIA-256-CBC-SHA384", | ||
903 | "ECDHE-RSA-CAMELLIA128-SHA256", "TLS-ECDHE-RSA-WITH-CAMELLIA-128-CBC-SHA256", | ||
904 | "ECDHE-RSA-CAMELLIA256-SHA384", "TLS-ECDHE-RSA-WITH-CAMELLIA-256-CBC-SHA384", | ||
905 | "ECDH-RSA-CAMELLIA128-SHA256", "TLS-ECDH-RSA-WITH-CAMELLIA-128-CBC-SHA256", | ||
906 | "ECDH-RSA-CAMELLIA256-SHA384", "TLS-ECDH-RSA-WITH-CAMELLIA-256-CBC-SHA384", | ||
907 | "PSK-CAMELLIA128-SHA256", "TLS-PSK-WITH-CAMELLIA-128-CBC-SHA256", | ||
908 | "PSK-CAMELLIA256-SHA384", "TLS-PSK-WITH-CAMELLIA-256-CBC-SHA384", | ||
909 | "DHE-PSK-CAMELLIA128-SHA256", "TLS-DHE-PSK-WITH-CAMELLIA-128-CBC-SHA256", | ||
910 | "DHE-PSK-CAMELLIA256-SHA384", "TLS-DHE-PSK-WITH-CAMELLIA-256-CBC-SHA384", | ||
911 | "RSA-PSK-CAMELLIA128-SHA256", "TLS-RSA-PSK-WITH-CAMELLIA-128-CBC-SHA256", | ||
912 | "RSA-PSK-CAMELLIA256-SHA384", "TLS-RSA-PSK-WITH-CAMELLIA-256-CBC-SHA384", | ||
913 | "ECDHE-PSK-CAMELLIA128-SHA256", "TLS-ECDHE-PSK-WITH-CAMELLIA-128-CBC-SHA256", | ||
914 | "ECDHE-PSK-CAMELLIA256-SHA384", "TLS-ECDHE-PSK-WITH-CAMELLIA-256-CBC-SHA384", | ||
915 | "AES128-CCM", "TLS-RSA-WITH-AES-128-CCM", | ||
916 | "AES256-CCM", "TLS-RSA-WITH-AES-256-CCM", | ||
917 | "DHE-RSA-AES128-CCM", "TLS-DHE-RSA-WITH-AES-128-CCM", | ||
918 | "DHE-RSA-AES256-CCM", "TLS-DHE-RSA-WITH-AES-256-CCM", | ||
919 | "AES128-CCM8", "TLS-RSA-WITH-AES-128-CCM-8", | ||
920 | "AES256-CCM8", "TLS-RSA-WITH-AES-256-CCM-8", | ||
921 | "DHE-RSA-AES128-CCM8", "TLS-DHE-RSA-WITH-AES-128-CCM-8", | ||
922 | "DHE-RSA-AES256-CCM8", "TLS-DHE-RSA-WITH-AES-256-CCM-8", | ||
923 | "PSK-AES128-CCM", "TLS-PSK-WITH-AES-128-CCM", | ||
924 | "PSK-AES256-CCM", "TLS-PSK-WITH-AES-256-CCM", | ||
925 | "DHE-PSK-AES128-CCM", "TLS-DHE-PSK-WITH-AES-128-CCM", | ||
926 | "DHE-PSK-AES256-CCM", "TLS-DHE-PSK-WITH-AES-256-CCM", | ||
927 | "PSK-AES128-CCM8", "TLS-PSK-WITH-AES-128-CCM-8", | ||
928 | "PSK-AES256-CCM8", "TLS-PSK-WITH-AES-256-CCM-8", | ||
929 | "DHE-PSK-AES128-CCM8", "TLS-PSK-DHE-WITH-AES-128-CCM-8", | ||
930 | "DHE-PSK-AES256-CCM8", "TLS-PSK-DHE-WITH-AES-256-CCM-8", | ||
931 | "ECDHE-ECDSA-AES128-CCM", "TLS-ECDHE-ECDSA-WITH-AES-128-CCM", | ||
932 | "ECDHE-ECDSA-AES256-CCM", "TLS-ECDHE-ECDSA-WITH-AES-256-CCM", | ||
933 | "ECDHE-ECDSA-AES128-CCM8", "TLS-ECDHE-ECDSA-WITH-AES-128-CCM-8", | ||
934 | "ECDHE-ECDSA-AES256-CCM8", "TLS-ECDHE-ECDSA-WITH-AES-256-CCM-8", | ||
935 | "ECDHE-RSA-CHACHA20-POLY1305-OLD", "TLS-ECDHE-RSA-WITH-CHACHA20-POLY1305-SHA256-OLD", | ||
936 | "ECDHE-ECDSA-CHACHA20-POLY1305-OLD", "TLS-ECDHE-ECDSA-WITH-CHACHA20-POLY1305-SHA256-OLD", | ||
937 | "DHE-RSA-CHACHA20-POLY1305-OLD", "TLS-DHE-RSA-WITH-CHACHA20-POLY1305-SHA256-OLD", | ||
938 | "ECDHE-RSA-CHACHA20-POLY1305", "TLS-ECDHE-RSA-WITH-CHACHA20-POLY1305-SHA256", | ||
939 | "ECDHE-ECDSA-CHACHA20-POLY1305", "TLS-ECDHE-ECDSA-WITH-CHACHA20-POLY1305-SHA256", | ||
940 | "DHE-RSA-CHACHA20-POLY1305", "TLS-DHE-RSA-WITH-CHACHA20-POLY1305-SHA256", | ||
941 | "PSK-CHACHA20-POLY1305", "TLS-PSK-WITH-CHACHA20-POLY1305-SHA256", | ||
942 | "ECDHE-PSK-CHACHA20-POLY1305", "TLS-ECDHE-PSK-WITH-CHACHA20-POLY1305-SHA256", | ||
943 | "DHE-PSK-CHACHA20-POLY1305", "TLS-DHE-PSK-WITH-CHACHA20-POLY1305-SHA256", | ||
944 | "RSA-PSK-CHACHA20-POLY1305", "TLS-RSA-PSK-WITH-CHACHA20-POLY1305-SHA256", | ||
945 | "GOST-MD5", "TLS-GOSTR341094-RSA-WITH-28147-CNT-MD5", | ||
946 | "GOST-GOST94", "TLS-RSA-WITH-28147-CNT-GOST94", | ||
947 | "RC4-MD5", "SSL-CK-RC4-128-WITH-MD5", | ||
948 | "EXP-RC4-MD5", "SSL-CK-RC4-128-EXPORT40-WITH-MD5", | ||
949 | "RC2-CBC-MD5", "SSL-CK-RC2-128-CBC-WITH-MD5", | ||
950 | "EXP-RC2-CBC-MD5", "SSL-CK-RC2-128-CBC-EXPORT40-WITH-MD5", | ||
951 | "IDEA-CBC-MD5", "SSL-CK-IDEA-128-CBC-WITH-MD5", | ||
952 | "DES-CBC-MD5", "SSL-CK-DES-64-CBC-WITH-MD5", | ||
953 | "DES-CBC-SHA", "SSL-CK-DES-64-CBC-WITH-SHA", | ||
954 | "DES-CBC3-MD5", "SSL-CK-DES-192-EDE3-CBC-WITH-MD5", | ||
955 | "DES-CBC3-SHA", "SSL-CK-DES-192-EDE3-CBC-WITH-SHA", | ||
956 | "RC4-64-MD5", "SSL-CK-RC4-64-WITH-MD5", | ||
957 | "DES-CFB-M1", "SSL-CK-DES-64-CFB64-WITH-MD5-1", | ||
958 | NULL | ||
959 | }; | ||
960 | // fprintf(stderr, "SUCCESS: %s => %s => %d\n\n", xlate_openssl[i], xlate_openssl[i+1], mbedtls_ssl_get_ciphersuite_id(xlate_openssl[i+1])); | ||
961 | static int map_openssl_suite(char *openssl_name) { | ||
962 | int i; | ||
963 | for (i = 0; xlate_openssl[i]; i += 2) { | ||
964 | if (!strcmp(xlate_openssl[i], openssl_name)) | ||
965 | return mbedtls_ssl_get_ciphersuite_id(xlate_openssl[i + 1]); | ||
966 | } | ||
967 | return 0; | ||
968 | } | ||
969 | |||
970 | #endif | ||
diff --git a/vchat-tls.h b/vchat-tls.h new file mode 100644 index 0000000..2771173 --- /dev/null +++ b/vchat-tls.h | |||
@@ -0,0 +1,58 @@ | |||
1 | #pragma once | ||
2 | |||
3 | /* prototypes */ | ||
4 | |||
5 | typedef int (*vc_askpass_cb_t)(char *, int, int, void *); | ||
6 | struct vc_x509store_t { | ||
7 | char *cafile; | ||
8 | char *capath; | ||
9 | char *crlfile; | ||
10 | vc_askpass_cb_t askpass_callback; | ||
11 | char *certfile; | ||
12 | char *keyfile; | ||
13 | int flags; | ||
14 | }; | ||
15 | typedef struct vc_x509store_t vc_x509store_t; | ||
16 | |||
17 | void vc_x509store_set_pkeycb(vc_x509store_t *, vc_askpass_cb_t); | ||
18 | void vc_x509store_setflags(vc_x509store_t *, int); | ||
19 | void vc_x509store_setkeyfile(vc_x509store_t *, char *); | ||
20 | void vc_x509store_setcertfile(vc_x509store_t *, char *); | ||
21 | void vc_x509store_setcafile(vc_x509store_t *, char *); | ||
22 | void vc_x509store_clearflags(vc_x509store_t *, int); | ||
23 | void vc_x509store_setcapath(vc_x509store_t *, char *); | ||
24 | void vc_x509store_setcrlfile(vc_x509store_t *, char *); | ||
25 | void vc_cleanup_x509store(vc_x509store_t *s); | ||
26 | |||
27 | #if !defined(TLS_LIB_OPENSSL) && !defined(TLS_LIB_MBEDTLS) | ||
28 | #error \ | ||
29 | "Neither TLS_LIB_OPENSSL nor TLS_LIB_MBEDTLS are defined. Please select at least one." | ||
30 | #endif | ||
31 | |||
32 | #ifdef TLS_LIB_OPENSSL | ||
33 | void vc_openssl_init_x509store(vc_x509store_t *); | ||
34 | int vc_openssl_connect(int serverfd, vc_x509store_t *); | ||
35 | ssize_t vc_openssl_sendmessage(const void *buf, size_t size); | ||
36 | ssize_t vc_openssl_receivemessage(void *buf, size_t size); | ||
37 | void vc_openssl_cleanup(); | ||
38 | char *vc_openssl_version(); | ||
39 | #endif | ||
40 | |||
41 | #ifdef TLS_LIB_MBEDTLS | ||
42 | void vc_mbedtls_init_x509store(vc_x509store_t *); | ||
43 | int vc_mbedtls_connect(int serverfd, vc_x509store_t *); | ||
44 | ssize_t vc_mbedtls_sendmessage(const void *buf, size_t size); | ||
45 | ssize_t vc_mbedtls_receivemessage(void *buf, size_t size); | ||
46 | void vc_mbedtls_cleanup(); | ||
47 | char *vc_mbedtls_version(); | ||
48 | #endif | ||
49 | |||
50 | #define VC_X509S_USE_CAFILE 0x01 | ||
51 | #define VC_X509S_USE_CAPATH 0x02 | ||
52 | #define VC_X509S_USE_CERTIFICATE 0x04 | ||
53 | #define VC_X509S_SSL_VERIFY_NONE 0x10 | ||
54 | #define VC_X509S_SSL_VERIFY_PEER 0x20 | ||
55 | #define VC_X509S_SSL_VERIFY_FAIL_IF_NO_PEER_CERT 0x40 | ||
56 | #define VC_X509S_SSL_VERIFY_CLIENT_ONCE 0x80 | ||
57 | #define VC_X509S_SSL_VERIFY_MASK 0xF0 | ||
58 | |||
diff --git a/vchat-ui.c b/vchat-ui.c index 82a6840..185cad0 100755..100644 --- a/vchat-ui.c +++ b/vchat-ui.c | |||
@@ -10,116 +10,124 @@ | |||
10 | * without even the implied warranty of merchantability or fitness for a | 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 | 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 | 12 | * any direct, indirect, incidental or special damages arising in any way out |
13 | * of the use of this software. | 13 | * of the use of this software. |
14 | * | 14 | * |
15 | */ | 15 | */ |
16 | 16 | ||
17 | /* general includes */ | 17 | /* general includes */ |
18 | #include <unistd.h> | 18 | #include <errno.h> |
19 | #include <stdint.h> | ||
20 | #include <ncurses.h> | 19 | #include <ncurses.h> |
20 | #include <readline/history.h> | ||
21 | #include <readline/readline.h> | ||
22 | #include <regex.h> | ||
21 | #include <signal.h> | 23 | #include <signal.h> |
24 | #include <stdint.h> | ||
22 | #include <stdlib.h> | 25 | #include <stdlib.h> |
26 | #include <string.h> | ||
23 | #include <strings.h> | 27 | #include <strings.h> |
24 | #include <errno.h> | ||
25 | #include <termios.h> | ||
26 | #include <sys/ioctl.h> | 28 | #include <sys/ioctl.h> |
29 | #include <termios.h> | ||
27 | #include <time.h> | 30 | #include <time.h> |
28 | #include <string.h> | 31 | #include <unistd.h> |
29 | #include <readline/readline.h> | ||
30 | #include <readline/history.h> | ||
31 | #include <regex.h> | ||
32 | #include <wchar.h> | 32 | #include <wchar.h> |
33 | 33 | ||
34 | #include "vchat.h" | ||
35 | #include "vchat-user.h" | 34 | #include "vchat-user.h" |
35 | #include "vchat.h" | ||
36 | 36 | ||
37 | /* version of this module */ | 37 | /* version of this module */ |
38 | const char *vchat_ui_version = "vchat-ui.c $Id$"; | 38 | const char *vchat_ui_version = |
39 | "vchat-ui.c $Id$"; | ||
39 | 40 | ||
40 | /* externally used variables */ | 41 | /* externally used variables */ |
41 | /* current string in topic window */ | 42 | /* current string in topic window */ |
42 | char topicstr[TOPICSTRSIZE] = "[] VChat 0.19"; | 43 | char topicstr[TOPICSTRSIZE] = "[] VChat 0.20"; |
43 | /* current string in console window */ | 44 | /* current string in console window */ |
44 | char consolestr[CONSOLESTRSIZE] = "[ Get help: .h for server /h for client commands"; | 45 | char consolestr[CONSOLESTRSIZE] = |
46 | "[ Get help: .h for server /h for client commands"; | ||
45 | 47 | ||
46 | static unsigned int ui_init = 0; | 48 | static unsigned int ui_init = 0; |
47 | 49 | ||
48 | /* our windows */ | 50 | /* our windows */ |
49 | static WINDOW *console = NULL; | 51 | static WINDOW *console = NULL; |
50 | static WINDOW *input = NULL; | 52 | static WINDOW *input = NULL; |
51 | static WINDOW *topic = NULL; | 53 | static WINDOW *topic = NULL; |
52 | static WINDOW *channel = NULL; | 54 | static WINDOW *channel = NULL; |
53 | static WINDOW *private = NULL; | 55 | static WINDOW *private = NULL; |
54 | static WINDOW *output = NULL; | 56 | static WINDOW *output = NULL; |
55 | 57 | ||
56 | /* our screen dimensions */ | 58 | /* our screen dimensions */ |
57 | static int screensx = 0; | 59 | static int screensx = 0; |
58 | static int screensy = 0; | 60 | static int screensy = 0; |
59 | /* current horizontal scrolling offset for input line */ | 61 | /* current horizontal scrolling offset for input line */ |
60 | static int scroff = 0; | 62 | static int scroff = 0; |
61 | /* cache for stepping value of horizontal scrolling */ | 63 | /* cache for stepping value of horizontal scrolling */ |
62 | unsigned int hscroll = 0; | 64 | unsigned int hscroll = 0; |
63 | 65 | ||
64 | static int outputshown = 0; | 66 | static int outputshown = 0; |
65 | static int outputwidth_desired = 0; | 67 | static int outputwidth_desired = 0; |
66 | 68 | ||
67 | static int privheight = 0; | 69 | static int privheight = 0; |
68 | static int privheight_desired = 0; | 70 | static int privheight_desired = 0; |
69 | static int privwinhidden = 0; | 71 | static int privwinhidden = 0; |
70 | int usetime = 1; | 72 | int usetime = 1; |
71 | int outputcountdown = 0; | 73 | int outputcountdown = 0; |
72 | char *querypartner = NULL; | 74 | char *querypartner = NULL; |
73 | 75 | ||
74 | struct sb_entry { | 76 | struct sb_entry { |
75 | int id; | 77 | int id; |
76 | time_t when; | 78 | time_t when; |
77 | int stamp; | 79 | int stamp; |
78 | char *what; | 80 | char *what; |
79 | struct sb_entry *link; | 81 | struct sb_entry *link; |
80 | }; | 82 | }; |
81 | 83 | ||
82 | struct sb_data { | 84 | struct sb_data { |
83 | struct sb_entry *entries; | 85 | struct sb_entry *entries; |
84 | struct sb_entry *last; | 86 | struct sb_entry *last; |
85 | int count; | 87 | int count; |
86 | int scroll; | 88 | int scroll; |
87 | }; | 89 | }; |
88 | 90 | ||
89 | static struct sb_data *sb_pub = NULL; | 91 | static struct sb_data *sb_pub = NULL; |
90 | static struct sb_data *sb_priv = NULL; | 92 | static struct sb_data *sb_priv = NULL; |
91 | static struct sb_data *sb_out = NULL; | 93 | static struct sb_data *sb_out = NULL; |
94 | static struct sb_data *sb_connect = NULL; | ||
92 | 95 | ||
93 | /* Tells, which window is active */ | 96 | /* Tells, which window is active */ |
94 | static int sb_win = 0; /* 0 for pub, 1 for priv */ | 97 | static int sb_win = 0; /* 0 for pub, 1 for priv */ |
95 | 98 | ||
96 | /* struct to keep filter list */ | 99 | /* struct to keep filter list */ |
97 | struct filt { | 100 | struct filt { |
98 | char colour; | 101 | char colour; |
99 | unsigned int id; | 102 | unsigned int id; |
100 | char *text; | 103 | char *text; |
101 | regex_t regex; | 104 | regex_t regex; |
102 | struct filt *next; | 105 | struct filt *next; |
103 | }; | 106 | }; |
104 | 107 | ||
105 | typedef struct filt filt; | 108 | typedef struct filt filt; |
106 | 109 | ||
107 | static filt *filterlist = NULL; | 110 | static filt *filterlist = NULL; |
108 | static int filtertype = 0; | 111 | static int filtertype = 0; |
109 | static int currentstamp = 0; | 112 | static int currentstamp = 0; |
110 | 113 | ||
111 | /* Prototype declarations */ | 114 | /* Prototype declarations */ |
112 | 115 | ||
113 | static void resize (int); | 116 | static void resize(int); |
114 | static void forceredraw (void); | 117 | static void forceredraw(void); |
115 | static void forceredraw_wrapper (int a) {forceredraw();} | 118 | static void forceredraw_wrapper(int a) { |
116 | static void drawwin (WINDOW *win, struct sb_data *sb); | 119 | (void)a; |
117 | static int writescr (WINDOW *win, struct sb_entry *entry); | 120 | forceredraw(); |
118 | static int testfilter ( struct sb_entry *entry); | 121 | } |
119 | static int gettextwidth (const char *textbuffer); | 122 | static void drawwin(WINDOW *win, struct sb_data *sb); |
120 | static void resize_output (void); | 123 | static int writescr(WINDOW *win, struct sb_entry *entry); |
121 | static int getsbeheight (struct sb_entry *entry, const int xwidth, int needstime ); | 124 | static int testfilter(struct sb_entry *entry); |
122 | static int getsbdataheight (struct sb_data *data, const int xwidth, int needstime ); | 125 | static int gettextwidth(const char *textbuffer); |
126 | static void resize_output(void); | ||
127 | static int getsbeheight(struct sb_entry *entry, const int xwidth, | ||
128 | int needstime); | ||
129 | static int getsbdataheight(struct sb_data *data, const int xwidth, | ||
130 | int needstime); | ||
123 | /* CURRENTLY UNUSED | 131 | /* CURRENTLY UNUSED |
124 | static void writecolorized (WINDOW *win, char *string); | 132 | static void writecolorized (WINDOW *win, char *string); |
125 | */ | 133 | */ |
@@ -132,22 +140,29 @@ enum { | |||
132 | }; | 140 | }; |
133 | 141 | ||
134 | /* */ | 142 | /* */ |
135 | static void | 143 | static void togglequery() { |
136 | togglequery() { | 144 | if (querypartner && private) { |
137 | if( querypartner && private ) { | 145 | { |
138 | { struct sb_data *tmp = sb_pub; sb_pub = sb_priv; sb_priv = tmp; } | 146 | struct sb_data *tmp = sb_pub; |
139 | { WINDOW *tmp= private; private = channel; channel = tmp; } | 147 | sb_pub = sb_priv; |
148 | sb_priv = tmp; | ||
149 | } | ||
150 | { | ||
151 | WINDOW *tmp = private; | ||
152 | private | ||
153 | = channel; | ||
154 | channel = tmp; | ||
155 | } | ||
140 | } | 156 | } |
141 | } | 157 | } |
142 | 158 | ||
143 | const char * skip_to_character( const char * string, size_t offset ) { | 159 | const char *skip_to_character(const char *string, size_t offset) { |
144 | size_t ch_size; | 160 | size_t ch_size; |
145 | mbstate_t mbs; | 161 | mbstate_t mbs; |
146 | memset(&mbs, 0, sizeof(mbs)); | 162 | memset(&mbs, 0, sizeof(mbs)); |
147 | 163 | ||
148 | while( offset-- > 0 ) { | 164 | while (offset-- > 0) { |
149 | switch( ch_size = mbrlen( string, MB_CUR_MAX, &mbs ) ) | 165 | switch (ch_size = mbrlen(string, MB_CUR_MAX, &mbs)) { |
150 | { | ||
151 | case (size_t)-1: | 166 | case (size_t)-1: |
152 | case (size_t)-2: | 167 | case (size_t)-2: |
153 | return NULL; | 168 | return NULL; |
@@ -161,15 +176,14 @@ const char * skip_to_character( const char * string, size_t offset ) { | |||
161 | return string; | 176 | return string; |
162 | } | 177 | } |
163 | 178 | ||
164 | size_t offset_to_character( const char * string, size_t offset ) { | 179 | size_t offset_to_character(const char *string, size_t offset) { |
165 | mbstate_t mbs; | 180 | mbstate_t mbs; |
166 | memset(&mbs, 0, sizeof(mbs)); | 181 | memset(&mbs, 0, sizeof(mbs)); |
167 | const char * string_offset = string + offset; | 182 | const char *string_offset = string + offset; |
168 | size_t ch_size, nchars = 0; | 183 | size_t ch_size, nchars = 0; |
169 | 184 | ||
170 | while( string < string_offset ) { | 185 | while (string < string_offset) { |
171 | switch( ch_size = mbrlen( string, MB_CUR_MAX, &mbs ) ) | 186 | switch (ch_size = mbrlen(string, MB_CUR_MAX, &mbs)) { |
172 | { | ||
173 | case (size_t)-1: | 187 | case (size_t)-1: |
174 | case (size_t)-2: | 188 | case (size_t)-2: |
175 | return -1; | 189 | return -1; |
@@ -184,191 +198,195 @@ size_t offset_to_character( const char * string, size_t offset ) { | |||
184 | } | 198 | } |
185 | 199 | ||
186 | /* readlines callback when a line is completed */ | 200 | /* readlines callback when a line is completed */ |
187 | static void | 201 | static void linecomplete(char *line) { |
188 | linecomplete (char *line) | ||
189 | { | ||
190 | char *c; | 202 | char *c; |
191 | int i; | 203 | int i; |
192 | 204 | ||
193 | /* send linefeed, return pointer, reset cursors */ | 205 | /* send linefeed, return pointer, reset cursors */ |
194 | waddch (input, '\n'); | 206 | waddch(input, '\n'); |
195 | wmove (input, 0, 0); | 207 | wmove(input, 0, 0); |
196 | scroff = 0; | 208 | scroff = 0; |
197 | 209 | ||
198 | if (line) { | 210 | if (line) { |
199 | i = strlen(line) - 1; | 211 | i = strlen(line) - 1; |
200 | while (line[i] == ' ') line[i--]='\0'; | 212 | while (line[i] == ' ') |
213 | line[i--] = '\0'; | ||
201 | 214 | ||
202 | if (line[0] && strchr(line,' ') == NULL && line[i] == ':') | 215 | if (line[0] && strchr(line, ' ') == NULL && line[i] == ':') |
203 | line[i--] = '\0'; | 216 | line[i--] = '\0'; |
204 | 217 | ||
205 | /* empty line? nada. */ | 218 | /* empty line? nada. */ |
206 | if (line[0]) { | 219 | if (line[0]) { |
207 | /* add line to history and have it handled in vchat-protocol.c */ | 220 | /* add line to history and have it handled in vchat-protocol.c */ |
208 | add_history (line); | 221 | add_history(line); |
209 | handleline (line); | 222 | handleline(line); |
210 | } | 223 | } |
211 | free (line); | 224 | free(line); |
212 | rl_reset_line_state(); | 225 | rl_reset_line_state(); |
213 | rl_point = rl_end = rl_done = 0; | 226 | rl_point = rl_end = rl_done = 0; |
214 | 227 | ||
215 | /* If in query mode, feed query prefix */ | 228 | /* If in query mode, feed query prefix */ |
216 | if (( c = querypartner )) | 229 | if ((c = querypartner)) |
217 | while( *c ) rl_stuff_char( *c++ ); | 230 | while (*c) |
231 | rl_stuff_char(*c++); | ||
218 | 232 | ||
219 | /* wipe input line and reset cursor */ | 233 | /* wipe input line and reset cursor */ |
220 | wrefresh (input); | 234 | wrefresh(input); |
221 | } | 235 | } |
222 | } | 236 | } |
223 | 237 | ||
224 | /* redraw-callback for readline */ | 238 | /* redraw-callback for readline */ |
225 | static void | 239 | static void vciredraw(void) { |
226 | vciredraw (void) | ||
227 | { | ||
228 | int i; | 240 | int i; |
229 | size_t readline_point; | 241 | size_t readline_point; |
230 | 242 | ||
231 | /* readline offers us information we don't need | 243 | /* readline offers us information we don't need |
232 | so ignore outabound cursor positions */ | 244 | so ignore outabound cursor positions */ |
233 | if( rl_point < 0 ) rl_point = 0; | 245 | if (rl_point < 0) |
234 | //if( rl_point > rl_end ) rl_point = rl_end; | 246 | rl_point = 0; |
247 | // if( rl_point > rl_end ) rl_point = rl_end; | ||
235 | 248 | ||
236 | readline_point = offset_to_character( rl_line_buffer, rl_point ); | 249 | readline_point = offset_to_character(rl_line_buffer, rl_point); |
237 | 250 | ||
238 | /* hscroll value cache set up? */ | 251 | /* hscroll value cache set up? */ |
239 | if (!hscroll) { | 252 | if (!hscroll) { |
240 | /* check config-option or set hardcoded default */ | 253 | /* check config-option or set hardcoded default */ |
241 | hscroll = getintoption (CF_HSCROLL); | 254 | hscroll = getintoption(CF_HSCROLL); |
242 | if (!hscroll) | 255 | if (!hscroll) |
243 | hscroll = 15; | 256 | hscroll = 15; |
244 | } | 257 | } |
245 | 258 | ||
246 | /* calculate horizontal scrolling offset */ | 259 | /* calculate horizontal scrolling offset */ |
247 | 260 | ||
248 | /* Case 1: readline is left of current scroll offset: Adjust to left to reveal more text */ | 261 | /* Case 1: readline is left of current scroll offset: Adjust to left to reveal |
249 | if( readline_point < scroff ) | 262 | * more text */ |
263 | if (readline_point < scroff) | ||
250 | scroff = readline_point - hscroll; | 264 | scroff = readline_point - hscroll; |
251 | if( scroff < 1 ) | 265 | if (scroff < 1) |
252 | scroff = 0; | 266 | scroff = 0; |
253 | 267 | ||
254 | /* Case 2: readline just hit the last char on the line: Adjust to right to leave more space on screen */ | 268 | /* Case 2: readline just hit the last char on the line: Adjust to right to |
255 | if( readline_point >= scroff + getmaxx(input) - 1 ) | 269 | * leave more space on screen */ |
270 | if (readline_point >= scroff + getmaxx(input) - 1) | ||
256 | scroff = readline_point - getmaxx(input) + hscroll; | 271 | scroff = readline_point - getmaxx(input) + hscroll; |
257 | 272 | ||
258 | /* wipe input line */ | 273 | /* wipe input line */ |
259 | wmove (input, 0, 0); | 274 | wmove(input, 0, 0); |
260 | for (i = 0; i < getmaxx(input) - 1; i++) | 275 | for (i = 0; i < getmaxx(input) - 1; i++) |
261 | waddch (input, ' '); | 276 | waddch(input, ' '); |
262 | 277 | ||
263 | /* show current line, move cursor, redraw! */ | 278 | /* show current line, move cursor, redraw! */ |
264 | const char *start_line = skip_to_character( rl_line_buffer, scroff ); | 279 | const char *start_line = skip_to_character(rl_line_buffer, scroff); |
265 | const char *end_line = skip_to_character( start_line, getmaxx(input) - 1 ); | 280 | const char *end_line = skip_to_character(start_line, getmaxx(input) - 1); |
266 | |||
267 | mvwaddnstr (input, 0, 0, start_line, end_line - start_line ); | ||
268 | wmove (input, 0, readline_point - scroff ); | ||
269 | wrefresh (input); | ||
270 | 281 | ||
282 | mvwaddnstr(input, 0, 0, start_line, end_line - start_line); | ||
283 | wmove(input, 0, readline_point - scroff); | ||
284 | wrefresh(input); | ||
271 | } | 285 | } |
272 | 286 | ||
273 | /* called by the eventloop in vchat-client.c */ | 287 | /* called by the eventloop in vchat-client.c */ |
274 | void | 288 | void userinput(void) { |
275 | userinput (void) | ||
276 | { | ||
277 | /* let readline handle what the user typed .. */ | 289 | /* let readline handle what the user typed .. */ |
278 | rl_callback_read_char (); | 290 | rl_callback_read_char(); |
279 | } | 291 | } |
280 | 292 | ||
281 | static int | 293 | static int calcdrawcus(char *const str) { |
282 | calcdrawcus (char * const str) { | ||
283 | char *tmp = str; | 294 | char *tmp = str; |
284 | int zero = 0; | 295 | int zero = 0; |
285 | while( *tmp && (*tmp!=' ') && (*tmp!='\n')) { if(*tmp==1) zero+=2; tmp++; } | 296 | while (*tmp && (*tmp != ' ') && (*tmp != '\n')) { |
297 | if (*tmp == 1) | ||
298 | zero += 2; | ||
299 | tmp++; | ||
300 | } | ||
286 | return (tmp - str) - zero; | 301 | return (tmp - str) - zero; |
287 | } | 302 | } |
288 | 303 | ||
289 | static void | 304 | static void sb_flush(struct sb_data *sb) { |
290 | sb_flush ( struct sb_data *sb ) { | ||
291 | struct sb_entry *now = sb->entries, *prev = NULL, *tmp; | 305 | struct sb_entry *now = sb->entries, *prev = NULL, *tmp; |
292 | while( now ) { | 306 | while (now) { |
293 | tmp = (struct sb_entry*)((unsigned long)prev ^ (unsigned long)now->link); | 307 | tmp = (struct sb_entry *)((unsigned long)prev ^ (unsigned long)now->link); |
294 | free(now->what ); | 308 | free(now->what); |
295 | free(now); | 309 | free(now); |
296 | prev = now; | 310 | prev = now; |
297 | now = tmp; | 311 | now = tmp; |
298 | } | 312 | } |
299 | sb->entries = NULL; | 313 | sb->entries = NULL; |
314 | sb->last = NULL; | ||
315 | sb->count = 0; | ||
316 | sb->scroll = 0; | ||
300 | } | 317 | } |
301 | 318 | ||
302 | /*static void | 319 | /* |
303 | sb_clear ( struct sb_data **sb ) { | 320 | static void sb_clear ( struct sb_data **sb ) { |
304 | sb_flush(*sb); | 321 | sb_flush(*sb); |
305 | free( *sb ); | 322 | free( *sb ); |
306 | *sb = NULL; | 323 | *sb = NULL; |
307 | }*/ | 324 | } |
308 | 325 | */ | |
309 | static struct sb_entry* | 326 | |
310 | sb_add (struct sb_data *sb, const char *line, time_t when) { | 327 | static struct sb_entry *sb_add(struct sb_data *sb, const char *line, |
311 | struct sb_entry *newone = malloc (sizeof(struct sb_entry)); | 328 | time_t when) { |
312 | if( newone ) { | 329 | struct sb_entry *newone = malloc(sizeof(struct sb_entry)); |
313 | if( sb->count == sb->scroll ) sb->scroll++; | 330 | if (newone) { |
314 | newone->id = sb->count++; | 331 | if (sb->count == sb->scroll) |
315 | newone->when = when; | 332 | sb->scroll++; |
316 | newone->what = strdup(line); | 333 | newone->id = sb->count++; |
317 | newone->link = sb->entries; | 334 | newone->when = when; |
318 | newone->stamp= 0xffff; | 335 | newone->what = strdup(line); |
319 | if( sb->entries ) | 336 | newone->link = sb->entries; |
320 | sb->entries->link = (struct sb_entry*)((unsigned long)sb->entries->link ^ (unsigned long)newone); | 337 | newone->stamp = 0xffff; |
321 | else | 338 | if (sb->entries) |
322 | sb->last = newone; | 339 | sb->entries->link = (struct sb_entry *)((unsigned long)sb->entries->link ^ |
323 | sb->entries = newone; | 340 | (unsigned long)newone); |
341 | else | ||
342 | sb->last = newone; | ||
343 | sb->entries = newone; | ||
324 | } | 344 | } |
325 | return newone; | 345 | return newone; |
326 | } | 346 | } |
327 | 347 | ||
328 | void flushout ( ) | 348 | void flushout() { |
329 | { | ||
330 | sb_flush(sb_out); | 349 | sb_flush(sb_out); |
331 | writeout(" "); | 350 | writeout(" "); |
332 | outputwidth_desired = 0; | 351 | outputwidth_desired = 0; |
333 | outputshown = 0; | 352 | outputshown = 0; |
334 | } | 353 | } |
335 | 354 | ||
336 | void hideout( ) | 355 | void hideout() { |
337 | { | 356 | if (outputshown) { |
338 | if( outputshown ) { | 357 | outputshown = 0; |
339 | outputshown = 0; | 358 | resize(0); |
340 | resize(0); | ||
341 | } | 359 | } |
342 | } | 360 | } |
343 | 361 | ||
344 | void showout (void) | 362 | void showout(void) { |
345 | { | ||
346 | writeout(" "); | 363 | writeout(" "); |
347 | outputcountdown = 6; | 364 | outputcountdown = 6; |
348 | outputshown = 1; | 365 | outputshown = 1; |
349 | resize(0); | 366 | resize(0); |
350 | } | 367 | } |
351 | 368 | ||
352 | void writeout (const char *str) | 369 | void writeout(const char *str) { |
353 | { | ||
354 | int i; | 370 | int i; |
355 | sb_add(sb_out,str,time(NULL)); | 371 | sb_add(sb_out, str, time(NULL)); |
356 | i = 1 + gettextwidth( str ); | 372 | i = 1 + gettextwidth(str); |
357 | if( i > outputwidth_desired ) outputwidth_desired = i; | 373 | if (i > outputwidth_desired) |
374 | outputwidth_desired = i; | ||
358 | } | 375 | } |
359 | 376 | ||
360 | int writechan (char *str) { | 377 | int writechan(char *str) { |
361 | struct sb_entry *tmp; | 378 | struct sb_entry *tmp; |
362 | int i = 0; | 379 | int i = 0; |
363 | time_t now = time(NULL); | 380 | time_t now = time(NULL); |
364 | tmp = sb_add(sb_pub,str,now); | 381 | tmp = sb_add(sb_pub, str, now); |
365 | 382 | ||
366 | if ( (sb_pub->scroll == sb_pub->count) && ((filtertype == 0) || ( testfilter(tmp)))) { | 383 | if ((sb_pub->scroll == sb_pub->count) && |
367 | i = writescr(channel, tmp); | 384 | ((filtertype == 0) || (testfilter(tmp)))) { |
368 | wnoutrefresh(channel); | 385 | i = writescr(channel, tmp); |
386 | wnoutrefresh(channel); | ||
369 | } | 387 | } |
370 | 388 | ||
371 | if( querypartner && private ) | 389 | if (querypartner && private) |
372 | topicline(NULL); | 390 | topicline(NULL); |
373 | else | 391 | else |
374 | consoleline(NULL); | 392 | consoleline(NULL); |
@@ -376,55 +394,73 @@ int writechan (char *str) { | |||
376 | return i; | 394 | return i; |
377 | } | 395 | } |
378 | 396 | ||
379 | int writecf (formtstr id, char *str) { | 397 | int writecf(formtstr id, char *str) { |
380 | struct sb_entry *tmp; | 398 | struct sb_entry *tmp; |
381 | int i = 0; | 399 | int i = 0; |
382 | time_t now = time(NULL); | 400 | time_t now = time(NULL); |
383 | snprintf(tmpstr,TMPSTRSIZE,getformatstr(id),str); | 401 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(id), str); |
384 | tmp = sb_add(sb_pub,tmpstr,now); | 402 | tmp = sb_add(sb_pub, tmpstr, now); |
385 | 403 | ||
386 | if ( (sb_pub->scroll == sb_pub->count) && | 404 | if ((sb_pub->scroll == sb_pub->count) && |
387 | ((filtertype == 0) || ( testfilter(tmp)))) { | 405 | ((filtertype == 0) || (testfilter(tmp)))) { |
388 | i = writescr(channel, tmp); | 406 | i = writescr(channel, tmp); |
389 | wnoutrefresh(channel); | 407 | wnoutrefresh(channel); |
390 | } | 408 | } |
391 | 409 | ||
392 | if( querypartner && private ) | 410 | if (querypartner && private) |
393 | topicline(NULL); | 411 | topicline(NULL); |
394 | else | 412 | else |
395 | consoleline(NULL); | 413 | consoleline(NULL); |
396 | 414 | ||
415 | if (!loggedin) | ||
416 | sb_add(sb_connect, str, now); | ||
417 | |||
397 | return i; | 418 | return i; |
398 | } | 419 | } |
399 | 420 | ||
400 | int writepriv (char *str, int maybeep) { | 421 | void dumpconnect() { |
422 | struct sb_entry *now = sb_connect->entries, *prev = NULL, *tmp; | ||
423 | while (now) { | ||
424 | tmp = (struct sb_entry *)((unsigned long)prev ^ (unsigned long)now->link); | ||
425 | fputs(now->what, stderr); | ||
426 | fputc(10, stderr); | ||
427 | prev = now; | ||
428 | now = tmp; | ||
429 | } | ||
430 | } | ||
431 | |||
432 | void flushconnect() { | ||
433 | sb_flush(sb_connect); | ||
434 | } | ||
435 | |||
436 | int writepriv(char *str, int maybeep) { | ||
401 | int i = 0; | 437 | int i = 0; |
402 | if (private) { | 438 | if (private) { |
403 | 439 | ||
404 | time_t now = time (NULL); | 440 | time_t now = time(NULL); |
405 | struct sb_entry *tmp; | 441 | struct sb_entry *tmp; |
406 | tmp = sb_add(sb_priv,str,now); | 442 | tmp = sb_add(sb_priv, str, now); |
407 | 443 | ||
408 | if ( !privwinhidden && (sb_priv->scroll == sb_priv->count) && | 444 | if (!privwinhidden && (sb_priv->scroll == sb_priv->count) && |
409 | ((filtertype == 0) || ( testfilter(tmp)))) { | 445 | ((filtertype == 0) || (testfilter(tmp)))) { |
410 | i = writescr(private, tmp); | 446 | i = writescr(private, tmp); |
411 | } | 447 | } |
412 | if( privwinhidden && !querypartner ) { | 448 | if (privwinhidden && !querypartner) { |
413 | if( (maybeep != 0) && (getintoption( CF_BELLPRIV ) != 0 )) | 449 | if ((maybeep != 0) && (getintoption(CF_BELLPRIV) != 0)) |
414 | putchar( 7 ); | 450 | putchar(7); |
415 | privheight_desired = privwinhidden; | 451 | privheight_desired = privwinhidden; |
416 | privwinhidden = 0; | 452 | privwinhidden = 0; |
417 | resize(0); | 453 | resize(0); |
418 | } | 454 | } |
419 | wnoutrefresh(private); | 455 | wnoutrefresh(private); |
420 | 456 | ||
421 | if( querypartner && private ) | 457 | if (querypartner && private) |
422 | consoleline(NULL); | 458 | consoleline(NULL); |
423 | else | 459 | else |
424 | topicline(NULL); | 460 | topicline(NULL); |
425 | 461 | ||
426 | } else | 462 | } else |
427 | i = writechan( str ); | 463 | i = writechan(str); |
428 | 464 | ||
429 | return i; | 465 | return i; |
430 | } | 466 | } |
@@ -434,338 +470,364 @@ int writepriv (char *str, int maybeep) { | |||
434 | #if NCURSES_VERSION_MAJOR >= 5 | 470 | #if NCURSES_VERSION_MAJOR >= 5 |
435 | 471 | ||
436 | typedef struct { | 472 | typedef struct { |
437 | attr_t attr; | 473 | attr_t attr; |
438 | short pair; | 474 | short pair; |
439 | } ncurs_attr; | 475 | } ncurs_attr; |
440 | 476 | ||
441 | #define WATTR_GET( win, orgattr ) wattr_get( win, &orgattr.attr, &orgattr.pair, NULL) | 477 | #define WATTR_GET(win, orgattr) \ |
442 | #define WATTR_SET( win, orgattr ) wattr_set( win, orgattr.attr, orgattr.pair, NULL) | 478 | wattr_get(win, &orgattr.attr, &orgattr.pair, NULL) |
443 | #define BCOLR_SET( attr, colour ) attr->pair = colour; | 479 | #define WATTR_SET(win, orgattr) wattr_set(win, orgattr.attr, orgattr.pair, NULL) |
480 | #define BCOLR_SET(attr, colour) attr->pair = colour; | ||
444 | 481 | ||
445 | #else | 482 | #else |
446 | 483 | ||
447 | typedef struct { | 484 | typedef struct { |
448 | attr_t attr; | 485 | attr_t attr; |
449 | } ncurs_attr; | 486 | } ncurs_attr; |
450 | 487 | ||
451 | #define WATTR_GET( win, orgattr ) orgattr.attr = wattr_get(win); | 488 | #define WATTR_GET(win, orgattr) orgattr.attr = wattr_get(win); |
452 | #define WATTR_SET( win, orgattr ) wattr_set( win, orgattr.attr); | 489 | #define WATTR_SET(win, orgattr) wattr_set(win, orgattr.attr); |
453 | #define BCOLR_SET( attr, colour ) attr->attr = ((attr->attr) & ~A_COLOR) | COLOR_PAIR((colour)); | 490 | #define BCOLR_SET(attr, colour) \ |
491 | attr->attr = ((attr->attr) & ~A_COLOR) | COLOR_PAIR((colour)); | ||
454 | 492 | ||
455 | #endif | 493 | #endif |
456 | 494 | ||
457 | /* 'A' - 'Z' attriute type */ | 495 | /* 'A' - 'Z' attriute type */ |
458 | static int attributes[] = { A_ALTCHARSET, A_BOLD, 0, A_DIM, 0, 0, 0, 0, A_INVIS, 0, 0, A_BLINK, | 496 | static int attributes[] = {A_ALTCHARSET, |
459 | 0, A_NORMAL, 0, A_PROTECT, 0, A_REVERSE, A_STANDOUT, 0, A_UNDERLINE, | 497 | A_BOLD, |
460 | 0, 0, 1, 0, 0 }; | 498 | 0, |
461 | 499 | A_DIM, | |
462 | static void | 500 | 0, |
463 | docolorize (char colour, ncurs_attr *attr, ncurs_attr orgattr) { | 501 | 0, |
464 | if( colour== '0') { | 502 | 0, |
465 | *attr = orgattr; | 503 | 0, |
466 | } else if( ( colour > '0') && ( colour <= '9')) { | 504 | A_INVIS, |
467 | BCOLR_SET( attr, colour - '0' ); | 505 | 0, |
506 | 0, | ||
507 | A_BLINK, | ||
508 | 0, | ||
509 | A_NORMAL, | ||
510 | 0, | ||
511 | A_PROTECT, | ||
512 | 0, | ||
513 | A_REVERSE, | ||
514 | A_STANDOUT, | ||
515 | 0, | ||
516 | A_UNDERLINE, | ||
517 | 0, | ||
518 | 0, | ||
519 | 1, | ||
520 | 0, | ||
521 | 0}; | ||
522 | |||
523 | static void docolorize(char colour, ncurs_attr *attr, ncurs_attr orgattr) { | ||
524 | if (colour == '0') { | ||
525 | *attr = orgattr; | ||
526 | } else if ((colour > '0') && (colour <= '9')) { | ||
527 | BCOLR_SET(attr, colour - '0'); | ||
468 | } else { | 528 | } else { |
469 | char upc = colour & ( 0x20 ^ 0xff ); /* colour AND NOT 0x20 */ | 529 | char upc = colour & (0x20 ^ 0xff); /* colour AND NOT 0x20 */ |
470 | attr_t newattr; | 530 | attr_t newattr; |
471 | if( ( upc >= 'A') && ( upc<='Z' ) && ( newattr = attributes[upc - 'A']) ) | 531 | if ((upc >= 'A') && (upc <= 'Z') && (newattr = attributes[upc - 'A'])) |
472 | attr->attr = ( colour & 0x20 ) ? attr->attr | newattr : attr->attr & ~newattr; | 532 | attr->attr = |
533 | (colour & 0x20) ? attr->attr | newattr : attr->attr & ~newattr; | ||
473 | } | 534 | } |
474 | } | 535 | } |
475 | 536 | ||
476 | /* draw arbitrary strings */ | 537 | /* draw arbitrary strings */ |
477 | static int | 538 | static int writescr(WINDOW *win, struct sb_entry *entry) { |
478 | writescr ( WINDOW *win, struct sb_entry *entry ) { | 539 | char tmp[64]; |
479 | char tmp [64]; | ||
480 | int charcount = 0; | 540 | int charcount = 0; |
481 | int i; | 541 | int i; |
482 | int textlen = strlen( entry->what ); | 542 | int textlen = strlen(entry->what); |
483 | int timelen = ((win == channel)||(win == private)) && usetime ? | 543 | int timelen = ((win == channel) || (win == private)) && usetime |
484 | (int)strftime(tmp,64,getformatstr(FS_TIME),localtime(&entry->when)) : 0; | 544 | ? (int)strftime(tmp, 64, getformatstr(FS_TIME), |
485 | char textbuffer[ textlen+timelen+1 ]; | 545 | localtime(&entry->when)) |
486 | ncurs_attr attrbuffer[ textlen+timelen+1 ]; | 546 | : 0; |
487 | ncurs_attr orgattr; | 547 | char textbuffer[textlen + timelen + 1]; |
548 | ncurs_attr attrbuffer[textlen + timelen + 1]; | ||
549 | ncurs_attr orgattr; | ||
488 | 550 | ||
489 | /* illegal window? return */ | 551 | /* illegal window? return */ |
490 | if( !win || !(textlen+timelen)) return 0; | 552 | if (!win || !(textlen + timelen)) |
553 | return 0; | ||
491 | 554 | ||
492 | /* store original attributes */ | 555 | /* store original attributes */ |
493 | WATTR_GET( win, orgattr); | 556 | WATTR_GET(win, orgattr); |
494 | attrbuffer[ 0 ] = orgattr; | 557 | attrbuffer[0] = orgattr; |
495 | 558 | ||
496 | /* copy time string */ | 559 | /* copy time string */ |
497 | for( i = 0; i < timelen; i++ ) | 560 | for (i = 0; i < timelen; i++) |
498 | if( tmp[ i ] == 1 ) { | 561 | if (tmp[i] == 1) { |
499 | docolorize( tmp[++i], attrbuffer+charcount, orgattr); | 562 | docolorize(tmp[++i], attrbuffer + charcount, orgattr); |
500 | } else { | 563 | } else { |
501 | attrbuffer[ charcount+1 ] = attrbuffer[ charcount ]; | 564 | attrbuffer[charcount + 1] = attrbuffer[charcount]; |
502 | textbuffer[ charcount++ ] = tmp[ i ]; | 565 | textbuffer[charcount++] = tmp[i]; |
503 | } | 566 | } |
504 | 567 | ||
505 | timelen = charcount; | 568 | timelen = charcount; |
506 | 569 | ||
507 | /* copy text */ | 570 | /* copy text */ |
508 | for( i = 0; i< textlen; i++ ) | 571 | for (i = 0; i < textlen; i++) |
509 | if( entry->what[ i ] == 1 ) { | 572 | if (entry->what[i] == 1) { |
510 | docolorize( entry->what[++i], attrbuffer+charcount, orgattr); | 573 | docolorize(entry->what[++i], attrbuffer + charcount, orgattr); |
511 | } else { | 574 | } else { |
512 | attrbuffer[ charcount+1 ] = attrbuffer[ charcount ]; | 575 | attrbuffer[charcount + 1] = attrbuffer[charcount]; |
513 | textbuffer[ charcount++ ] = entry->what[ i ]; | 576 | textbuffer[charcount++] = entry->what[i]; |
514 | } | 577 | } |
515 | 578 | ||
516 | /* zero terminate text --- very important :) */ | 579 | /* zero terminate text --- very important :) */ |
517 | textbuffer[ charcount ] = 0; | 580 | textbuffer[charcount] = 0; |
518 | 581 | ||
519 | /* hilite */ | 582 | /* hilite */ |
520 | if((win == channel)||(win == private)) { /* do not higlight bars */ | 583 | if ((win == channel) || (win == private)) { /* do not higlight bars */ |
521 | filt *flt = filterlist; | 584 | filt *flt = filterlist; |
522 | char *instr = textbuffer; | 585 | char *instr = textbuffer; |
523 | regmatch_t match; | 586 | regmatch_t match; |
524 | int j; | 587 | int j; |
525 | 588 | ||
526 | while( flt ) { | 589 | while (flt) { |
527 | if ( (flt->colour != '-') && (flt->colour != '+')) { | 590 | if ((flt->colour != '-') && (flt->colour != '+')) { |
528 | i = timelen; | 591 | i = timelen; |
529 | while( i < charcount ) { | 592 | while (i < charcount) { |
530 | if( regexec( &flt->regex, instr+i, 1, &match, 0 )) { | 593 | if (regexec(&flt->regex, instr + i, 1, &match, 0)) { |
531 | i = charcount; | 594 | i = charcount; |
532 | } else { | 595 | } else { |
533 | for( j = i + match.rm_so; j < i + match.rm_eo; j++) | 596 | for (j = i + match.rm_so; j < i + match.rm_eo; j++) |
534 | docolorize( flt->colour, attrbuffer+j, orgattr ); | 597 | docolorize(flt->colour, attrbuffer + j, orgattr); |
535 | i += 1 + match.rm_so; | 598 | i += 1 + match.rm_so; |
536 | } | ||
537 | } | ||
538 | } | 599 | } |
539 | flt = flt->next; | 600 | } |
540 | } | 601 | } |
602 | flt = flt->next; | ||
603 | } | ||
541 | } | 604 | } |
542 | 605 | ||
543 | if (getcurx(win)) waddch(win,'\n'); | 606 | if (getcurx(win)) |
544 | 607 | waddch(win, '\n'); | |
545 | for( i = 0; i < charcount; i++ ) { | 608 | |
546 | /* on start of line or attribute changes set new attribute */ | 609 | for (i = 0; i < charcount; i++) { |
547 | if( !i || memcmp( attrbuffer+i, attrbuffer+i-1, sizeof(ncurs_attr))) | 610 | /* on start of line or attribute changes set new attribute */ |
548 | WATTR_SET( win, attrbuffer[i]); | 611 | if (!i || memcmp(attrbuffer + i, attrbuffer + i - 1, sizeof(ncurs_attr))) |
549 | if( textbuffer[ i ] == ' ') { | 612 | WATTR_SET(win, attrbuffer[i]); |
550 | if ((calcdrawcus(textbuffer+i+1) + getcurx(win) > getmaxx(win) - 1 - 1)&& | 613 | if (textbuffer[i] == ' ') { |
551 | (calcdrawcus(textbuffer+i+1) < getmaxx(win) - 1 )) { | 614 | if ((calcdrawcus(textbuffer + i + 1) + getcurx(win) > |
552 | /* line wrap found */ | 615 | getmaxx(win) - 1 - 1) && |
553 | WATTR_SET( win, orgattr); | 616 | (calcdrawcus(textbuffer + i + 1) < getmaxx(win) - 1)) { |
554 | waddstr( win, "\n "); | 617 | /* line wrap found */ |
555 | WATTR_SET( win, attrbuffer[ i ]); | 618 | WATTR_SET(win, orgattr); |
556 | } | 619 | waddstr(win, "\n "); |
620 | WATTR_SET(win, attrbuffer[i]); | ||
557 | } | 621 | } |
558 | /* plot character */ | 622 | } |
559 | waddch( win, (unsigned char)textbuffer[ i ]); | 623 | /* plot character */ |
624 | waddch(win, (unsigned char)textbuffer[i]); | ||
560 | } | 625 | } |
561 | 626 | ||
562 | /* restore old attributes */ | 627 | /* restore old attributes */ |
563 | WATTR_SET (win, orgattr); | 628 | WATTR_SET(win, orgattr); |
564 | 629 | ||
565 | return charcount; | 630 | return charcount; |
566 | } | 631 | } |
567 | 632 | ||
568 | static void | 633 | static void resize_output() { |
569 | resize_output ( ) | 634 | int outputwidth = |
570 | { | 635 | (outputwidth_desired + 7 > screensx) ? screensx - 7 : outputwidth_desired; |
571 | int outputwidth = (outputwidth_desired + 7 > screensx) ? screensx - 7 : outputwidth_desired; | 636 | int outputheight = getsbdataheight(sb_out, outputwidth - 1, 0); |
572 | int outputheight = getsbdataheight(sb_out, outputwidth-1, 0); | ||
573 | 637 | ||
574 | if (outputheight + 5 > screensy ) outputheight = screensy - 5; | 638 | if (outputheight + 5 > screensy) |
575 | wresize(output,outputheight,outputwidth); | 639 | outputheight = screensy - 5; |
576 | mvwin(output,(screensy-outputheight)>>1,(screensx-outputwidth)>>1); | 640 | wresize(output, outputheight, outputwidth); |
641 | mvwin(output, (screensy - outputheight) >> 1, (screensx - outputwidth) >> 1); | ||
577 | drawwin(output, sb_out); | 642 | drawwin(output, sb_out); |
578 | } | 643 | } |
579 | 644 | ||
580 | static void | 645 | static void doscroll(int up) { |
581 | doscroll ( int up ) { | ||
582 | togglequery(); | 646 | togglequery(); |
583 | { | 647 | { |
584 | WINDOW *destwin = (sb_win && private) ? private : channel; | 648 | WINDOW *destwin = (sb_win && private) ? private : channel; |
585 | struct sb_data *sb = (sb_win && private) ? sb_priv : sb_pub; | 649 | struct sb_data *sb = (sb_win && private) ? sb_priv : sb_pub; |
586 | struct sb_entry *now = sb->entries, *prev = NULL, *tmp; | 650 | struct sb_entry *now = sb->entries, *prev = NULL, *tmp; |
587 | int lines = (getmaxy(destwin) - 1 ) >>1; | 651 | int lines = (getmaxy(destwin) - 1) >> 1; |
588 | 652 | ||
589 | if( sb->scroll != sb->count ) | 653 | if (sb->scroll != sb->count) |
590 | while( now && (now->id != sb->scroll) ) { | 654 | while (now && (now->id != sb->scroll)) { |
591 | tmp = now; now = (struct sb_entry*)((unsigned long)now->link ^ (unsigned long)prev); prev = tmp; | 655 | tmp = now; |
656 | now = | ||
657 | (struct sb_entry *)((unsigned long)now->link ^ (unsigned long)prev); | ||
658 | prev = tmp; | ||
592 | } | 659 | } |
593 | 660 | ||
594 | if( !up ) { | 661 | if (!up) { |
595 | prev = (struct sb_entry*)((unsigned long)now->link ^ (unsigned long)prev); | 662 | prev = |
596 | tmp = now; now = (struct sb_entry*)((unsigned long)now->link ^ (unsigned long)prev); prev = tmp; | 663 | (struct sb_entry *)((unsigned long)now->link ^ (unsigned long)prev); |
597 | } | 664 | tmp = now; |
665 | now = (struct sb_entry *)((unsigned long)now->link ^ (unsigned long)prev); | ||
666 | prev = tmp; | ||
667 | } | ||
598 | 668 | ||
599 | while( now && (lines > 0)) { | 669 | while (now && (lines > 0)) { |
600 | if ( (!filtertype) || ( (now->stamp != currentstamp) && ( (now->stamp == (currentstamp | (1<<15))) || testfilter( now ) ) ) ) | 670 | if ((!filtertype) || |
601 | { | 671 | ((now->stamp != currentstamp) && |
602 | lines -= getsbeheight( now, getmaxx(destwin) - 1, usetime ); | 672 | ((now->stamp == (currentstamp | (1 << 15))) || testfilter(now)))) { |
673 | lines -= getsbeheight(now, getmaxx(destwin) - 1, usetime); | ||
603 | } | 674 | } |
604 | tmp = now; now = (struct sb_entry*)((unsigned long)now->link ^ (unsigned long)prev); prev = tmp; | 675 | tmp = now; |
605 | } | 676 | now = (struct sb_entry *)((unsigned long)now->link ^ (unsigned long)prev); |
606 | if( now ) | 677 | prev = tmp; |
678 | } | ||
679 | if (now) | ||
607 | sb->scroll = now->id; | 680 | sb->scroll = now->id; |
608 | else | 681 | else if (!up) |
609 | if( !up ) sb->scroll = sb->count; | 682 | sb->scroll = sb->count; |
610 | 683 | ||
611 | drawwin(destwin, sb); | 684 | drawwin(destwin, sb); |
612 | wnoutrefresh(destwin); | 685 | wnoutrefresh(destwin); |
613 | 686 | ||
614 | togglequery(); | 687 | togglequery(); |
615 | 688 | ||
616 | if( private && (destwin == channel) ) | 689 | if (private && (destwin == channel)) |
617 | consoleline( NULL); | 690 | consoleline(NULL); |
618 | else | 691 | else |
619 | topicline( NULL); | 692 | topicline(NULL); |
620 | } | 693 | } |
621 | } | 694 | } |
622 | 695 | ||
696 | void scrollup(void) { doscroll(1); } | ||
623 | 697 | ||
624 | void | 698 | void scrolldown(void) { doscroll(0); } |
625 | scrollup (void) | ||
626 | { | ||
627 | doscroll( 1 ); | ||
628 | } | ||
629 | |||
630 | void | ||
631 | scrolldown (void) | ||
632 | { | ||
633 | doscroll( 0 ); | ||
634 | } | ||
635 | 699 | ||
636 | void | 700 | void scrollwin(void) { |
637 | scrollwin (void) | 701 | if (!sb_win && private && !privwinhidden) |
638 | { | 702 | sb_win = 1; |
639 | if (!sb_win && private && !privwinhidden) sb_win = 1; | 703 | else |
640 | else sb_win = 0; | 704 | sb_win = 0; |
641 | topicline(NULL); | 705 | topicline(NULL); |
642 | consoleline(NULL); | 706 | consoleline(NULL); |
643 | } | 707 | } |
644 | 708 | ||
645 | void | 709 | void growprivwin(void) { |
646 | growprivwin (void) { | 710 | if (private) { |
647 | if( private ) { | 711 | if (privwinhidden) |
648 | if( privwinhidden) | 712 | privwinhidden = 0; |
649 | privwinhidden = 0; | 713 | if (++privheight_desired > screensy - 5) |
650 | if( ++privheight_desired > screensy - 5) | 714 | privheight_desired = screensy - 5; |
651 | privheight_desired = screensy - 5; | 715 | resize(0); |
652 | resize(0); | 716 | } |
653 | } | ||
654 | } | 717 | } |
655 | 718 | ||
656 | void toggleprivwin (void) { | 719 | void toggleprivwin(void) { |
657 | if( outputshown ) { | 720 | if (outputshown) { |
658 | outputshown = 0; | 721 | outputshown = 0; |
659 | resize(0); | 722 | resize(0); |
660 | } else { | 723 | } else { |
661 | if( private ) { | 724 | if (private) { |
662 | if( privwinhidden ) { | 725 | if (privwinhidden) { |
663 | privheight_desired = privwinhidden; | 726 | privheight_desired = privwinhidden; |
664 | privwinhidden = 0; | 727 | privwinhidden = 0; |
665 | } else { | 728 | } else { |
666 | privwinhidden = privheight_desired; | 729 | privwinhidden = privheight_desired; |
667 | privheight_desired = 1; | 730 | privheight_desired = 1; |
668 | sb_win = 0; | 731 | sb_win = 0; |
669 | sb_priv->scroll = sb_priv->count; | 732 | sb_priv->scroll = sb_priv->count; |
670 | } | 733 | } |
671 | resize(0); | 734 | resize(0); |
672 | } | 735 | } |
673 | } | 736 | } |
674 | } | 737 | } |
675 | 738 | ||
676 | void | 739 | void shrinkprivwin(void) { |
677 | shrinkprivwin (void) { | 740 | if (private && !privwinhidden) { |
678 | if( private && !privwinhidden ) { | 741 | if (--privheight_desired < 1) |
679 | if( --privheight_desired < 1) privheight_desired = 1; | 742 | privheight_desired = 1; |
680 | if( privheight_desired > screensy - 5) privheight_desired = screensy - 5; | 743 | if (privheight_desired > screensy - 5) |
744 | privheight_desired = screensy - 5; | ||
681 | resize(0); | 745 | resize(0); |
682 | } | 746 | } |
683 | } | 747 | } |
684 | 748 | ||
685 | /* clear message window */ | 749 | /* clear message window */ |
686 | void | 750 | void clearpriv() { |
687 | clearpriv () | ||
688 | { | ||
689 | WINDOW *dest = NULL; | 751 | WINDOW *dest = NULL; |
690 | /* do we have a private window? */ | 752 | /* do we have a private window? */ |
691 | if (private && !privwinhidden ) | 753 | if (private && !privwinhidden) |
692 | dest = private; | 754 | dest = private; |
693 | else | 755 | else |
694 | dest = channel; | 756 | dest = channel; |
695 | 757 | ||
696 | /* clear window, move cursor to bottom, redraw */ | 758 | /* clear window, move cursor to bottom, redraw */ |
697 | wclear (dest); | 759 | wclear(dest); |
698 | wmove (dest, getmaxy(dest) - 1, getmaxx(dest) - 1); | 760 | wmove(dest, getmaxy(dest) - 1, getmaxx(dest) - 1); |
699 | wrefresh (dest); | 761 | wrefresh(dest); |
700 | |||
701 | } | 762 | } |
702 | 763 | ||
703 | /* clear channel window */ | 764 | /* clear channel window */ |
704 | void | 765 | void clearchan() { |
705 | clearchan () | ||
706 | { | ||
707 | /* clear window, move cursor to bottom, redraw */ | 766 | /* clear window, move cursor to bottom, redraw */ |
708 | wclear (channel); | 767 | wclear(channel); |
709 | wmove (channel, getmaxy(channel) - 1, getmaxx(channel) - 1); | 768 | wmove(channel, getmaxy(channel) - 1, getmaxx(channel) - 1); |
710 | wrefresh (channel); | 769 | wrefresh(channel); |
711 | } | 770 | } |
712 | 771 | ||
713 | /* Get window size */ | 772 | /* Get window size */ |
714 | 773 | ||
715 | void ttgtsz(int *x,int *y) { | 774 | void ttgtsz(int *x, int *y) { |
716 | #ifdef TIOCGSIZE | 775 | #ifdef TIOCGSIZE |
717 | struct ttysize getit; | 776 | struct ttysize getit; |
718 | #else | 777 | #else |
719 | #ifdef TIOCGWINSZ | 778 | #ifdef TIOCGWINSZ |
720 | struct winsize getit; | 779 | struct winsize getit; |
721 | #endif | 780 | #endif |
722 | #endif | 781 | #endif |
723 | 782 | ||
724 | *x=0; *y=0; | 783 | *x = 0; |
784 | *y = 0; | ||
725 | #ifdef TIOCGSIZE | 785 | #ifdef TIOCGSIZE |
726 | if(ioctl(1,TIOCGSIZE,&getit)!= -1) { | 786 | if (ioctl(1, TIOCGSIZE, &getit) != -1) { |
727 | *x=getit.ts_cols; | 787 | *x = getit.ts_cols; |
728 | *y=getit.ts_lines; | 788 | *y = getit.ts_lines; |
729 | } | 789 | } |
730 | #else | 790 | #else |
731 | #ifdef TIOCGWINSZ | 791 | #ifdef TIOCGWINSZ |
732 | if(ioctl(1,TIOCGWINSZ,&getit)!= -1) { | 792 | if (ioctl(1, TIOCGWINSZ, &getit) != -1) { |
733 | *x=getit.ws_col; | 793 | *x = getit.ws_col; |
734 | *y=getit.ws_row; | 794 | *y = getit.ws_row; |
735 | } | 795 | } |
736 | #endif | 796 | #endif |
737 | #endif | 797 | #endif |
738 | } | 798 | } |
739 | 799 | ||
740 | static void | 800 | static void forceredraw(void) { |
741 | forceredraw (void) | ||
742 | { | ||
743 | sb_pub->scroll = sb_pub->count; | 801 | sb_pub->scroll = sb_pub->count; |
744 | sb_priv->scroll = sb_priv->count; | 802 | sb_priv->scroll = sb_priv->count; |
745 | 803 | ||
746 | resize(0); | 804 | resize(0); |
747 | if(console) wclear(console); | 805 | if (console) |
748 | if(topic) wclear(topic); | 806 | wclear(console); |
749 | if(private) wclear(private); | 807 | if (topic) |
750 | if(channel) wclear(channel ); | 808 | wclear(topic); |
751 | if(output) wclear(output); | 809 | if (private) |
752 | if(input) wclear(input); | 810 | wclear(private); |
811 | if (channel) | ||
812 | wclear(channel); | ||
813 | if (output) | ||
814 | wclear(output); | ||
815 | if (input) | ||
816 | wclear(input); | ||
753 | resize(0); | 817 | resize(0); |
754 | |||
755 | } | 818 | } |
756 | 819 | ||
757 | /* resize display on SIGWINCH | 820 | /* resize display on SIGWINCH |
758 | Nowadays used as our main redraw trigger engine */ | 821 | Nowadays used as our main redraw trigger engine */ |
759 | void | 822 | void resize(int signal) { |
760 | resize (int signal) | 823 | int xsize, ysize, topicheight = topic ? 1 : 0; |
761 | { | 824 | (void)signal; |
762 | int xsize,ysize,topicheight=topic?1:0; | ||
763 | 825 | ||
764 | ttgtsz(&xsize,&ysize); | 826 | ttgtsz(&xsize, &ysize); |
765 | resizeterm(ysize,xsize); | 827 | resizeterm(ysize, xsize); |
766 | 828 | ||
767 | /* store screen-dimensions to local functions */ | 829 | /* store screen-dimensions to local functions */ |
768 | getmaxyx (stdscr, screensy, screensx); | 830 | getmaxyx(stdscr, screensy, screensx); |
769 | 831 | ||
770 | /* desired height of PM window is user controllable, | 832 | /* desired height of PM window is user controllable, |
771 | actual size depends on space available on screen */ | 833 | actual size depends on space available on screen */ |
@@ -774,24 +836,34 @@ resize (int signal) | |||
774 | 836 | ||
775 | /* Leave at least 5 lines for input, console and | 837 | /* Leave at least 5 lines for input, console and |
776 | pubchannel */ | 838 | pubchannel */ |
777 | if ( privheight_desired > screensy - 5) | 839 | if (privheight_desired > screensy - 5) |
778 | privheight = screensy - 5; | 840 | privheight = screensy - 5; |
779 | else | 841 | else |
780 | privheight = privheight_desired; | 842 | privheight = privheight_desired; |
781 | 843 | ||
782 | /* check dimensions or bump user */ | 844 | /* check dimensions or bump user */ |
783 | if (screensy - privheight < 4) | 845 | if (screensy - privheight < 4) { |
784 | { | 846 | fprintf(stderr, |
785 | fprintf (stderr, "vchat-client: screen to short (only %d rows, at least %d expected), bailing out.\n", screensy, privheight + 6); | 847 | "vchat-client: screen to short (only %d rows, at least %d " |
786 | snprintf (errstr, ERRSTRSIZE, "vchat-client: screen to short (only %d rows, at least %d expected), bailing out.\n", screensy, privheight + 6); | 848 | "expected), bailing out.\n", |
787 | cleanup (0); | 849 | screensy, privheight + 6); |
788 | } | 850 | snprintf(errstr, ERRSTRSIZE, |
789 | if (screensx < 14) | 851 | "vchat-client: screen to short (only %d rows, at least %d " |
790 | { | 852 | "expected), bailing out.\n", |
791 | fprintf (stderr, "vchat-client: screen to thin (only %d cols, at least %d expected), bailing out.\n", screensx, 14); | 853 | screensy, privheight + 6); |
792 | snprintf (errstr, ERRSTRSIZE, "vchat-client: screen to thin (only %d cols, at least %d expected), bailing out.\n", screensx, 14); | 854 | cleanup(0); |
793 | cleanup (0); | 855 | } |
794 | } | 856 | if (screensx < 14) { |
857 | fprintf(stderr, | ||
858 | "vchat-client: screen to thin (only %d cols, at least %d " | ||
859 | "expected), bailing out.\n", | ||
860 | screensx, 14); | ||
861 | snprintf(errstr, ERRSTRSIZE, | ||
862 | "vchat-client: screen to thin (only %d cols, at least %d " | ||
863 | "expected), bailing out.\n", | ||
864 | screensx, 14); | ||
865 | cleanup(0); | ||
866 | } | ||
795 | 867 | ||
796 | /***** | 868 | /***** |
797 | * Arrange windows on screen | 869 | * Arrange windows on screen |
@@ -800,38 +872,44 @@ resize (int signal) | |||
800 | togglequery(); | 872 | togglequery(); |
801 | 873 | ||
802 | /* console and input are always there and always 1 line tall */ | 874 | /* console and input are always there and always 1 line tall */ |
803 | wresize(console,1,screensx); | 875 | wresize(console, 1, screensx); |
804 | wresize(input,1,screensx); | 876 | wresize(input, 1, screensx); |
805 | 877 | ||
806 | /* If we got a private window and it is not hidden, set its size */ | 878 | /* If we got a private window and it is not hidden, set its size */ |
807 | if (private && !privwinhidden) | 879 | if (private && !privwinhidden) |
808 | wresize(private,privheight,screensx); | 880 | wresize(private, privheight, screensx); |
809 | 881 | ||
810 | /* If oldschool vchat is not enabled, we have a topic line */ | 882 | /* If oldschool vchat is not enabled, we have a topic line */ |
811 | if( topic ) | 883 | if (topic) |
812 | wresize(topic,1,screensx); | 884 | wresize(topic, 1, screensx); |
813 | 885 | ||
814 | /* public channel is always there and its height depends on: | 886 | /* public channel is always there and its height depends on: |
815 | * existence and visibility of priv window | 887 | * existence and visibility of priv window |
816 | * existence of a topic line (oldschool vchat style) | 888 | * existence of a topic line (oldschool vchat style) |
817 | */ | 889 | */ |
818 | wresize(channel, ( !private || privwinhidden ) ? screensy - ( topicheight + 2 ) : screensy - (privheight + ( topicheight + 2 )), screensx); | 890 | wresize(channel, |
891 | (!private || privwinhidden) | ||
892 | ? screensy - (topicheight + 2) | ||
893 | : screensy - (privheight + (topicheight + 2)), | ||
894 | screensx); | ||
819 | 895 | ||
820 | /* Console and input alway take bottommost lines */ | 896 | /* Console and input alway take bottommost lines */ |
821 | mvwin(console,screensy-2,0); | 897 | mvwin(console, screensy - 2, 0); |
822 | mvwin(input,screensy-1,0); | 898 | mvwin(input, screensy - 1, 0); |
823 | 899 | ||
824 | /* Private window always is top left */ | 900 | /* Private window always is top left */ |
825 | if(private && !privwinhidden) | 901 | if (private && !privwinhidden) |
826 | mvwin(private,0,0); | 902 | mvwin(private, 0, 0); |
827 | 903 | ||
828 | /* Topic window may not exist without priv window, so it is | 904 | /* Topic window may not exist without priv window, so it is |
829 | safe to assume sane values for privwinhidden and privheight */ | 905 | safe to assume sane values for privwinhidden and privheight */ |
830 | if( topic ) | 906 | if (topic) |
831 | mvwin(topic,privwinhidden ? 0 : privheight, 0); | 907 | mvwin(topic, privwinhidden ? 0 : privheight, 0); |
832 | 908 | ||
833 | /* chan window starts below private window and topic line */ | 909 | /* chan window starts below private window and topic line */ |
834 | mvwin(channel, ( !private || privwinhidden ) ? topicheight : privheight + topicheight, 0); | 910 | mvwin(channel, |
911 | (!private || privwinhidden) ? topicheight : privheight + topicheight, | ||
912 | 0); | ||
835 | 913 | ||
836 | /******* | 914 | /******* |
837 | * Now actual redraw starts, note, that we only fill | 915 | * Now actual redraw starts, note, that we only fill |
@@ -844,96 +922,106 @@ resize (int signal) | |||
844 | ******/ | 922 | ******/ |
845 | 923 | ||
846 | /* pub channel is always there, paint scrollback buffers */ | 924 | /* pub channel is always there, paint scrollback buffers */ |
847 | drawwin(channel, sb_pub); | 925 | drawwin(channel, sb_pub); |
848 | /* if priv exists and is visible, paint scrollback buffers */ | 926 | /* if priv exists and is visible, paint scrollback buffers */ |
849 | if(private && !privwinhidden ) | 927 | if (private && !privwinhidden) |
850 | drawwin(private, sb_priv); | 928 | drawwin(private, sb_priv); |
851 | /* Send window's contents to curses virtual buffers */ | 929 | /* Send window's contents to curses virtual buffers */ |
852 | wnoutrefresh(channel); | 930 | wnoutrefresh(channel); |
853 | if(private && !privwinhidden ) | 931 | if (private && !privwinhidden) |
854 | wnoutrefresh(private); | 932 | wnoutrefresh(private); |
855 | 933 | ||
856 | togglequery(); | 934 | togglequery(); |
857 | 935 | ||
858 | /* Resize and draw our message window, render topic and | 936 | /* Resize and draw our message window, render topic and |
859 | console line */ | 937 | console line */ |
860 | if(outputshown) resize_output(); | 938 | if (outputshown) |
861 | if(topic) topicline(NULL); | 939 | resize_output(); |
862 | consoleline(NULL); | 940 | if (topic) |
863 | if(loggedin) vciredraw(); | 941 | topicline(NULL); |
942 | consoleline(NULL); | ||
943 | if (loggedin) | ||
944 | vciredraw(); | ||
864 | } | 945 | } |
865 | 946 | ||
866 | static int | 947 | static int gettextwidth(const char *textbuffer) { |
867 | gettextwidth (const char *textbuffer) | 948 | int width = 0; |
868 | { | ||
869 | int width = 0; | ||
870 | 949 | ||
871 | do switch( *(textbuffer++) ) { | 950 | do |
872 | case 1: | 951 | switch (*(textbuffer++)) { |
873 | if (!*(textbuffer++)) return width; | 952 | case 1: |
874 | break; | 953 | if (!*(textbuffer++)) |
875 | case 0: | 954 | return width; |
955 | break; | ||
956 | case 0: | ||
876 | return width; | 957 | return width; |
877 | break; | 958 | break; |
878 | default: | 959 | default: |
879 | width++; | 960 | width++; |
880 | break; | 961 | break; |
881 | } while( 1 ); | 962 | } |
963 | while (1); | ||
882 | } | 964 | } |
883 | 965 | ||
884 | static int | 966 | static int getsbdataheight(struct sb_data *data, const int xwidth, |
885 | getsbdataheight (struct sb_data *data, const int xwidth, int needstime ) | 967 | int needstime) { |
886 | { | ||
887 | struct sb_entry *now = data->entries, *prev = NULL, *tmp; | 968 | struct sb_entry *now = data->entries, *prev = NULL, *tmp; |
888 | int height = 0; | 969 | int height = 0; |
889 | while( now ) { | 970 | while (now) { |
890 | height += getsbeheight( now, xwidth, needstime); | 971 | height += getsbeheight(now, xwidth, needstime); |
891 | tmp = now; now = (struct sb_entry*)((unsigned long)now->link ^ (unsigned long)prev); prev = tmp; | 972 | tmp = now; |
973 | now = (struct sb_entry *)((unsigned long)now->link ^ (unsigned long)prev); | ||
974 | prev = tmp; | ||
892 | } | 975 | } |
893 | return height; | 976 | return height; |
894 | } | 977 | } |
895 | 978 | ||
896 | static int | 979 | static int getsbeheight(struct sb_entry *entry, const int xwidth, |
897 | getsbeheight (struct sb_entry *entry, const int xwidth, int needstime ) | 980 | int needstime) { |
898 | { | ||
899 | int curx = 0, lines = 1; | 981 | int curx = 0, lines = 1; |
900 | char tmp[ 64 ], *textbuffer; | 982 | char tmp[64], *textbuffer; |
901 | 983 | ||
902 | if( needstime ) { | 984 | if (needstime) { |
903 | int timelen = (int)strftime(tmp,64,getformatstr(FS_TIME),localtime(&entry->when)); | 985 | int timelen = |
904 | tmp[ timelen ] = 2; | 986 | (int)strftime(tmp, 64, getformatstr(FS_TIME), localtime(&entry->when)); |
905 | textbuffer=tmp; | 987 | tmp[timelen] = 2; |
988 | textbuffer = tmp; | ||
906 | } else { | 989 | } else { |
907 | textbuffer = entry->what; | 990 | textbuffer = entry->what; |
908 | } | 991 | } |
909 | 992 | ||
910 | do switch( *textbuffer++ ) { | 993 | do |
911 | case ' ': | 994 | switch (*textbuffer++) { |
995 | case ' ': | ||
912 | if ((calcdrawcus(textbuffer) + curx > xwidth - 1) && | 996 | if ((calcdrawcus(textbuffer) + curx > xwidth - 1) && |
913 | (calcdrawcus(textbuffer) < xwidth)) { | 997 | (calcdrawcus(textbuffer) < xwidth)) { |
914 | lines++; curx = 4; | 998 | lines++; |
915 | } else { | 999 | curx = 4; |
916 | if( curx++ == xwidth ) { | 1000 | } else { |
917 | curx = 0; lines++; | 1001 | if (curx++ == xwidth) { |
918 | } | 1002 | curx = 0; |
919 | } | 1003 | lines++; |
1004 | } | ||
1005 | } | ||
920 | break; | 1006 | break; |
921 | case 1: | 1007 | case 1: |
922 | if (!*textbuffer++) return lines; | 1008 | if (!*textbuffer++) |
923 | break; | 1009 | return lines; |
924 | case 0: | 1010 | break; |
1011 | case 0: | ||
925 | return lines; | 1012 | return lines; |
926 | break; | 1013 | break; |
927 | case 2: | 1014 | case 2: |
928 | textbuffer=entry->what; | 1015 | textbuffer = entry->what; |
929 | break; | 1016 | break; |
930 | default: | 1017 | default: |
931 | if( curx++ == xwidth ) { | 1018 | if (curx++ == xwidth) { |
932 | curx = 0; lines++; | 1019 | curx = 0; |
1020 | lines++; | ||
933 | } | 1021 | } |
934 | break; | 1022 | break; |
935 | } while( 1 ); | 1023 | } |
936 | 1024 | while (1); | |
937 | } | 1025 | } |
938 | 1026 | ||
939 | /* Check, which kind of filter we have to apply: | 1027 | /* Check, which kind of filter we have to apply: |
@@ -943,8 +1031,7 @@ getsbeheight (struct sb_entry *entry, const int xwidth, int needstime ) | |||
943 | If no, or only colouring rules have been found, | 1031 | If no, or only colouring rules have been found, |
944 | no line filtering applies. | 1032 | no line filtering applies. |
945 | */ | 1033 | */ |
946 | static int | 1034 | static int analyzefilters(void) { |
947 | analyzefilters( void ) { | ||
948 | filt *filters = filterlist; | 1035 | filt *filters = filterlist; |
949 | int type = 0; | 1036 | int type = 0; |
950 | 1037 | ||
@@ -961,96 +1048,106 @@ analyzefilters( void ) { | |||
961 | tested the line against. This Stamp | 1048 | tested the line against. This Stamp |
962 | is updated for each change to the | 1049 | is updated for each change to the |
963 | filter list */ | 1050 | filter list */ |
964 | if( ++currentstamp == 0x3fff ) currentstamp = 1; | 1051 | if (++currentstamp == 0x3fff) |
965 | 1052 | currentstamp = 1; | |
966 | while( (type!=1) && filters ) { | 1053 | |
967 | if( filters->colour == '-' ) type = 2; | 1054 | while ((type != 1) && filters) { |
968 | if( filters->colour == '+' ) type = 1; | 1055 | if (filters->colour == '-') |
969 | filters=filters->next; | 1056 | type = 2; |
1057 | if (filters->colour == '+') | ||
1058 | type = 1; | ||
1059 | filters = filters->next; | ||
970 | } | 1060 | } |
971 | return type; | 1061 | return type; |
972 | } | 1062 | } |
973 | 1063 | ||
974 | static int | 1064 | static int testfilter(struct sb_entry *entry) { |
975 | testfilter ( struct sb_entry* entry ) { | 1065 | int match = 0; |
976 | int match = 0; | 1066 | filt *filters = filterlist; |
977 | filt *filters = filterlist; | 1067 | char filtercolour = filtertype == 2 ? '-' : '+'; |
978 | char filtercolour = filtertype == 2 ? '-' : '+'; | ||
979 | 1068 | ||
980 | while( !match && filters ) { | 1069 | while (!match && filters) { |
981 | if( filters->colour == filtercolour ) | 1070 | if (filters->colour == filtercolour) |
982 | match = regexec( &filters->regex, entry->what, 0, NULL, 0 ) ? 0 : 1; | 1071 | match = regexec(&filters->regex, entry->what, 0, NULL, 0) ? 0 : 1; |
983 | filters=filters->next; | 1072 | filters = filters->next; |
984 | } | 1073 | } |
985 | match = ( filtertype == 2 ) ? ( 1 - match ) : match; | 1074 | match = (filtertype == 2) ? (1 - match) : match; |
986 | entry->stamp = (match << 15) | currentstamp; | 1075 | entry->stamp = (match << 15) | currentstamp; |
987 | 1076 | ||
988 | return match; | 1077 | return match; |
989 | } | 1078 | } |
990 | 1079 | ||
991 | static void | 1080 | static void drawwin(WINDOW *win, struct sb_data *sb) { |
992 | drawwin (WINDOW *win, struct sb_data *sb ) | ||
993 | { | ||
994 | if (win) { | 1081 | if (win) { |
995 | struct sb_entry *now = sb->entries, *prev = NULL, *tmp; | 1082 | struct sb_entry *now = sb->entries, *prev = NULL, *tmp; |
996 | struct sb_entry *vis[getmaxy(win)]; | 1083 | struct sb_entry *vis[getmaxy(win)]; |
997 | int sumlines = 0, sumbuffers = 0; | 1084 | int sumlines = 0, sumbuffers = 0; |
998 | 1085 | ||
999 | /* public scrollback */ | 1086 | /* public scrollback */ |
1000 | if( sb->scroll != sb->count ) | 1087 | if (sb->scroll != sb->count) |
1001 | while( now && (now->id != sb->scroll) ) { | 1088 | while (now && (now->id != sb->scroll)) { |
1002 | tmp = now; now = (struct sb_entry*)((unsigned long)now->link ^ (unsigned long)prev); prev = tmp; | 1089 | tmp = now; |
1003 | } | 1090 | now = |
1091 | (struct sb_entry *)((unsigned long)now->link ^ (unsigned long)prev); | ||
1092 | prev = tmp; | ||
1093 | } | ||
1004 | 1094 | ||
1005 | if( (win == output) || (filtertype == 0)) { | 1095 | if ((win == output) || (filtertype == 0)) { |
1006 | while( now && (sumlines <= getmaxy(win) - 1 )) { | 1096 | while (now && (sumlines <= getmaxy(win) - 1)) { |
1007 | sumlines += getsbeheight( now, getmaxx(win) - 1, ((win == channel)||(win == private)) && usetime ); | 1097 | sumlines += |
1008 | vis[ sumbuffers++ ] = now; | 1098 | getsbeheight(now, getmaxx(win) - 1, |
1009 | tmp = now; now = (struct sb_entry*)((unsigned long)now->link ^ (unsigned long)prev); prev = tmp; | 1099 | ((win == channel) || (win == private)) && usetime); |
1010 | } | 1100 | vis[sumbuffers++] = now; |
1011 | } else { | 1101 | tmp = now; |
1012 | while( now && (sumlines <= getmaxy(win) - 1 )) { | 1102 | now = |
1013 | 1103 | (struct sb_entry *)((unsigned long)now->link ^ (unsigned long)prev); | |
1014 | /* If stamp matches exactly, line has been filtered out, since top | 1104 | prev = tmp; |
1015 | bit off means hidden */ | 1105 | } |
1016 | if( now->stamp != currentstamp) { | 1106 | } else { |
1017 | 1107 | while (now && (sumlines <= getmaxy(win) - 1)) { | |
1018 | /* If stamp matches and has top bit set, it has been identified | 1108 | |
1019 | positively. Else stamp does not match and line has to be | 1109 | /* If stamp matches exactly, line has been filtered out, since top |
1020 | tested against filters, which updates stamp. */ | 1110 | bit off means hidden */ |
1021 | if( (now->stamp == (currentstamp | 0x8000) ) || testfilter( now )) | 1111 | if (now->stamp != currentstamp) { |
1022 | { | 1112 | |
1023 | sumlines += getsbeheight( now, getmaxx(win) - 1, ((win == channel)||(win == private)) && usetime ); | 1113 | /* If stamp matches and has top bit set, it has been identified |
1024 | vis[ sumbuffers++ ] = now; | 1114 | positively. Else stamp does not match and line has to be |
1025 | } | 1115 | tested against filters, which updates stamp. */ |
1026 | 1116 | if ((now->stamp == (currentstamp | 0x8000)) || testfilter(now)) { | |
1027 | } | 1117 | sumlines += |
1028 | tmp = now; now = (struct sb_entry*)((unsigned long)now->link ^ (unsigned long)prev); prev = tmp; | 1118 | getsbeheight(now, getmaxx(win) - 1, |
1119 | ((win == channel) || (win == private)) && usetime); | ||
1120 | vis[sumbuffers++] = now; | ||
1029 | } | 1121 | } |
1122 | } | ||
1123 | tmp = now; | ||
1124 | now = | ||
1125 | (struct sb_entry *)((unsigned long)now->link ^ (unsigned long)prev); | ||
1126 | prev = tmp; | ||
1030 | } | 1127 | } |
1128 | } | ||
1031 | 1129 | ||
1032 | /* If buffer is not completely filled, clear window */ | 1130 | /* If buffer is not completely filled, clear window */ |
1033 | if( sumlines < getmaxy(win) ) | 1131 | if (sumlines < getmaxy(win)) |
1034 | wclear(win); | 1132 | wclear(win); |
1035 | 1133 | ||
1036 | /* Move pointer to bottom to let curses scroll */ | 1134 | /* Move pointer to bottom to let curses scroll */ |
1037 | wmove(win, getmaxy(win) - 1, getmaxx(win) - 1); | 1135 | wmove(win, getmaxy(win) - 1, getmaxx(win) - 1); |
1038 | 1136 | ||
1039 | /* Plot visible lines */ | 1137 | /* Plot visible lines */ |
1040 | while (sumbuffers--) writescr( win, vis[sumbuffers] ); | 1138 | while (sumbuffers--) |
1139 | writescr(win, vis[sumbuffers]); | ||
1041 | } | 1140 | } |
1042 | } | 1141 | } |
1043 | 1142 | ||
1044 | /* initialize curses and display */ | 1143 | /* initialize curses and display */ |
1045 | void | 1144 | void initui(void) { |
1046 | initui (void) | ||
1047 | { | ||
1048 | Keymap keymap; | 1145 | Keymap keymap; |
1049 | 1146 | ||
1050 | /* init curses */ | 1147 | /* init curses */ |
1051 | if (!ui_init) { | 1148 | if (!ui_init) { |
1052 | initscr (); | 1149 | initscr(); |
1053 | ui_init = 1; | 1150 | ui_init = 1; |
1054 | } | 1151 | } |
1055 | 1152 | ||
1056 | /* install signalhandler */ | 1153 | /* install signalhandler */ |
@@ -1059,119 +1156,146 @@ initui (void) | |||
1059 | signal(SIGCONT, forceredraw_wrapper); | 1156 | signal(SIGCONT, forceredraw_wrapper); |
1060 | 1157 | ||
1061 | /* set options */ | 1158 | /* set options */ |
1062 | keypad (stdscr, TRUE); | 1159 | keypad(stdscr, TRUE); |
1063 | nonl (); | 1160 | nonl(); |
1064 | cbreak (); | 1161 | cbreak(); |
1065 | 1162 | ||
1066 | /* color or monochome display? */ | 1163 | /* color or monochome display? */ |
1067 | if (has_colors ()) | 1164 | if (has_colors()) { |
1068 | { | 1165 | /* start color and set a colorset */ |
1069 | /* start color and set a colorset */ | 1166 | start_color(); |
1070 | start_color (); | 1167 | use_default_colors(); |
1071 | use_default_colors(); | 1168 | init_pair(1, COLOR_RED, -1); |
1072 | init_pair (1, COLOR_RED, -1); | 1169 | init_pair(2, COLOR_GREEN, -1); |
1073 | init_pair (2, COLOR_GREEN, -1); | 1170 | init_pair(3, COLOR_YELLOW, -1); |
1074 | init_pair (3, COLOR_YELLOW, -1); | 1171 | init_pair(4, COLOR_BLUE, -1); |
1075 | init_pair (4, COLOR_BLUE, -1); | 1172 | init_pair(5, COLOR_MAGENTA, -1); |
1076 | init_pair (5, COLOR_MAGENTA, -1); | 1173 | init_pair(6, COLOR_CYAN, -1); |
1077 | init_pair (6, COLOR_CYAN, -1); | 1174 | init_pair(7, COLOR_WHITE, -1); |
1078 | init_pair (7, COLOR_WHITE, -1); | 1175 | init_pair(8, COLOR_WHITE, COLOR_RED); |
1079 | init_pair (8, COLOR_WHITE, COLOR_RED); | 1176 | init_pair(9, COLOR_WHITE, COLOR_BLUE); |
1080 | init_pair (9, COLOR_WHITE, COLOR_BLUE); | 1177 | init_pair(10, COLOR_WHITE, COLOR_BLACK); |
1081 | } | 1178 | } else { |
1082 | else | 1179 | /* monochrome, start color and set a colorset anyways */ |
1083 | { | 1180 | start_color(); |
1084 | /* monochrome, start color and set a colorset anyways */ | 1181 | init_pair(1, -1, -1); |
1085 | start_color (); | 1182 | init_pair(2, -1, -1); |
1086 | init_pair (1, -1, -1); | 1183 | init_pair(3, -1, -1); |
1087 | init_pair (2, -1, -1); | 1184 | init_pair(4, -1, -1); |
1088 | init_pair (3, -1, -1); | 1185 | init_pair(5, -1, -1); |
1089 | init_pair (4, -1, -1); | 1186 | init_pair(6, -1, -1); |
1090 | init_pair (5, -1, -1); | 1187 | init_pair(7, -1, -1); |
1091 | init_pair (6, -1, -1); | 1188 | init_pair(8, -1, -1); |
1092 | init_pair (7, -1, -1); | 1189 | init_pair(9, -1, -1); |
1093 | init_pair (8, -1, -1); | 1190 | init_pair(10, -1, -1); |
1094 | init_pair (9, -1, -1); | 1191 | } |
1095 | } | ||
1096 | 1192 | ||
1097 | /* store screen-dimensions to local functions */ | 1193 | /* store screen-dimensions to local functions */ |
1098 | getmaxyx (stdscr, screensy, screensx); | 1194 | getmaxyx(stdscr, screensy, screensx); |
1099 | 1195 | ||
1100 | if (!privheight_desired) privheight_desired = getintoption(CF_PRIVHEIGHT); | 1196 | if (!privheight_desired) |
1101 | if ( privheight_desired > screensy - 5) privheight = screensy - 5; else privheight = privheight_desired; | 1197 | privheight_desired = getintoption(CF_PRIVHEIGHT); |
1198 | if (privheight_desired > screensy - 5) | ||
1199 | privheight = screensy - 5; | ||
1200 | else | ||
1201 | privheight = privheight_desired; | ||
1102 | 1202 | ||
1103 | /* check dimensions or bump user */ | 1203 | /* check dimensions or bump user */ |
1104 | if (screensy - privheight < 4) | 1204 | if (screensy - privheight < 4) { |
1105 | { | 1205 | fprintf(stderr, |
1106 | fprintf (stderr, "vchat-client: screen to short (only %d rows, at least %d expected), bailing out.\n", screensy, privheight + 6); | 1206 | "vchat-client: screen to short (only %d rows, at least %d " |
1107 | snprintf (errstr, ERRSTRSIZE, "vchat-client: screen to short (only %d rows, at least %d expected), bailing out.\n", screensy, privheight + 6); | 1207 | "expected), bailing out.\n", |
1108 | cleanup (0); | 1208 | screensy, privheight + 6); |
1109 | } | 1209 | snprintf(errstr, ERRSTRSIZE, |
1110 | if (screensx < 14) | 1210 | "vchat-client: screen to short (only %d rows, at least %d " |
1111 | { | 1211 | "expected), bailing out.\n", |
1112 | fprintf (stderr, "vchat-client: screen to thin (only %d cols, at least %d expected), bailing out.\n", screensx, 14); | 1212 | screensy, privheight + 6); |
1113 | snprintf (errstr, ERRSTRSIZE, "vchat-client: screen to thin (only %d cols, at least %d expected), bailing out.\n", screensx, 14); | 1213 | cleanup(0); |
1114 | cleanup (0); | 1214 | } |
1115 | } | 1215 | if (screensx < 14) { |
1216 | fprintf(stderr, | ||
1217 | "vchat-client: screen to thin (only %d cols, at least %d " | ||
1218 | "expected), bailing out.\n", | ||
1219 | screensx, 14); | ||
1220 | snprintf(errstr, ERRSTRSIZE, | ||
1221 | "vchat-client: screen to thin (only %d cols, at least %d " | ||
1222 | "expected), bailing out.\n", | ||
1223 | screensx, 14); | ||
1224 | cleanup(0); | ||
1225 | } | ||
1116 | 1226 | ||
1117 | /* setup our windows */ | 1227 | /* setup our windows */ |
1118 | console = newwin (1, screensx, screensy - 2, 0); | 1228 | console = newwin(1, screensx, screensy - 2, 0); |
1119 | input = newwin (1, screensx, screensy - 1, 0); | 1229 | input = newwin(1, screensx, screensy - 1, 0); |
1120 | if (privheight) private = newwin (privheight, screensx, 0, 0); | 1230 | if (privheight) |
1121 | if( private || getintoption(CF_USETOPIC)) | 1231 | private |
1122 | topic = newwin (1, screensx, privheight, 0); | 1232 | = newwin(privheight, screensx, 0, 0); |
1123 | channel = newwin (screensy - (privheight+3), screensx, (privheight+1), 0); | 1233 | if (private || getintoption(CF_USETOPIC)) |
1124 | output = newwin (1, screensx, 1, 0); | 1234 | topic = newwin(1, screensx, privheight, 0); |
1235 | channel = newwin(screensy - (privheight + 3), screensx, (privheight + 1), 0); | ||
1236 | output = newwin(1, screensx, 1, 0); | ||
1125 | 1237 | ||
1126 | /* promblems opening windows? bye! */ | 1238 | /* promblems opening windows? bye! */ |
1127 | if (!console || !input || (!topic && getintoption(CF_USETOPIC))|| !channel || !output || ( !private && privheight )) | 1239 | if (!console || !input || (!topic && getintoption(CF_USETOPIC)) || !channel || |
1128 | { | 1240 | !output || (!private && privheight)) { |
1129 | fprintf (stderr, "vchat-client: could not open windows, bailing out.\n"); | 1241 | fprintf(stderr, "vchat-client: could not open windows, bailing out.\n"); |
1130 | cleanup (0); | 1242 | cleanup(0); |
1131 | } | 1243 | } |
1132 | 1244 | ||
1133 | /* Prepare our scrollback buffers */ | 1245 | /* Prepare our scrollback buffers */ |
1134 | sb_pub = (struct sb_data*)malloc( sizeof(struct sb_data)); | 1246 | sb_pub = (struct sb_data *)malloc(sizeof(struct sb_data)); |
1135 | sb_out = (struct sb_data*)malloc( sizeof(struct sb_data)); | 1247 | sb_out = (struct sb_data *)malloc(sizeof(struct sb_data)); |
1136 | if( privheight) | 1248 | sb_connect = (struct sb_data *)malloc(sizeof(struct sb_data)); |
1137 | sb_priv = (struct sb_data*)malloc( sizeof(struct sb_data)); | 1249 | if (privheight) |
1250 | sb_priv = (struct sb_data *)malloc(sizeof(struct sb_data)); | ||
1138 | else | 1251 | else |
1139 | sb_priv = sb_pub; | 1252 | sb_priv = sb_pub; |
1140 | 1253 | ||
1141 | memset( sb_pub, 0, sizeof(struct sb_data)); | 1254 | memset(sb_pub, 0, sizeof(struct sb_data)); |
1142 | memset( sb_priv, 0, sizeof(struct sb_data)); | 1255 | memset(sb_priv, 0, sizeof(struct sb_data)); |
1143 | memset( sb_out, 0, sizeof(struct sb_data)); | 1256 | memset(sb_out, 0, sizeof(struct sb_data)); |
1257 | memset(sb_connect, 0, sizeof(struct sb_data)); | ||
1144 | 1258 | ||
1145 | /* set colors for windows */ | 1259 | /* set colors for windows */ |
1146 | if (has_colors()) { | 1260 | if (has_colors()) { |
1147 | wattrset (console, COLOR_PAIR (9)); | 1261 | if (getintoption(CF_INVWINBAR)) { |
1148 | wattrset (input, COLOR_PAIR (0)); | 1262 | wbkgd(console, COLOR_PAIR(0)); |
1149 | wbkgd (output, COLOR_PAIR(8)); | 1263 | wattron(console, A_REVERSE); |
1150 | wbkgd (console, COLOR_PAIR (9)); | 1264 | } else { |
1151 | wbkgd (channel, COLOR_PAIR (0)); | 1265 | wattrset(console, COLOR_PAIR(9)); |
1152 | wbkgd (input, COLOR_PAIR (0)); | 1266 | wbkgd(console, COLOR_PAIR(9)); |
1153 | if (private) | 1267 | } |
1154 | wbkgd (private, COLOR_PAIR (0)); | 1268 | wattrset(input, COLOR_PAIR(0)); |
1155 | if( topic ) { | 1269 | wbkgd(output, COLOR_PAIR(8)); |
1156 | wattrset (topic, COLOR_PAIR (9)); | 1270 | wbkgd(channel, COLOR_PAIR(0)); |
1157 | wbkgd (topic, COLOR_PAIR (9)); | 1271 | wbkgd(input, COLOR_PAIR(0)); |
1158 | } | 1272 | if (private) |
1273 | wbkgd(private, COLOR_PAIR(0)); | ||
1274 | if (topic) { | ||
1275 | if (getintoption(CF_INVWINBAR)) { | ||
1276 | wbkgd(input, COLOR_PAIR(0)); | ||
1277 | wattron(topic, A_REVERSE); | ||
1278 | } else { | ||
1279 | wattrset(topic, COLOR_PAIR(9)); | ||
1280 | wbkgd(topic, COLOR_PAIR(9)); | ||
1281 | } | ||
1282 | } | ||
1159 | } else { | 1283 | } else { |
1160 | wattron (console, A_REVERSE); | 1284 | wattron(console, A_REVERSE); |
1161 | wattron (output, A_REVERSE); | 1285 | wattron(output, A_REVERSE); |
1162 | wbkgd(output, A_REVERSE); | 1286 | wbkgd(output, A_REVERSE); |
1163 | if( topic ) | 1287 | if (topic) |
1164 | wattron (topic, A_REVERSE); | 1288 | wattron(topic, A_REVERSE); |
1165 | } | 1289 | } |
1166 | 1290 | ||
1167 | /* set some options */ | 1291 | /* set some options */ |
1168 | scrollok (channel, TRUE); | 1292 | scrollok(channel, TRUE); |
1169 | if (private) | 1293 | if (private) |
1170 | scrollok (private, TRUE); | 1294 | scrollok(private, TRUE); |
1171 | scrollok (input, TRUE); | 1295 | scrollok(input, TRUE); |
1172 | scrollok (output, TRUE); | 1296 | scrollok(output, TRUE); |
1173 | //idlok(channel,TRUE); | 1297 | // idlok(channel,TRUE); |
1174 | wtimeout (input, 100); | 1298 | wtimeout(input, 100); |
1175 | 1299 | ||
1176 | /* setup readlines display */ | 1300 | /* setup readlines display */ |
1177 | /* FIXME: present only in newer readline versions | 1301 | /* FIXME: present only in newer readline versions |
@@ -1182,87 +1306,88 @@ initui (void) | |||
1182 | rl_redisplay_function = vciredraw; | 1306 | rl_redisplay_function = vciredraw; |
1183 | 1307 | ||
1184 | /* get keymap, throw out unwanted binding */ | 1308 | /* get keymap, throw out unwanted binding */ |
1185 | keymap = rl_get_keymap (); | 1309 | keymap = rl_get_keymap(); |
1186 | rl_unbind_command_in_map ("clear-screen", keymap); | 1310 | rl_unbind_command_in_map("clear-screen", keymap); |
1187 | rl_unbind_command_in_map ("complete", keymap); | 1311 | rl_unbind_command_in_map("complete", keymap); |
1188 | rl_unbind_command_in_map ("possible-completions", keymap); | 1312 | rl_unbind_command_in_map("possible-completions", keymap); |
1189 | rl_unbind_command_in_map ("insert-completions", keymap); | 1313 | rl_unbind_command_in_map("insert-completions", keymap); |
1190 | 1314 | ||
1191 | /* bind CTRL-L to clearmsg() */ | 1315 | /* bind CTRL-L to clearmsg() */ |
1192 | rl_bind_key ('J'-'@', (rl_command_func_t *) clearpriv); | 1316 | rl_bind_key('J' - '@', (rl_command_func_t *)clearpriv); |
1193 | rl_bind_key ('O'-'@', (rl_command_func_t *) clearchan); | 1317 | rl_bind_key('O' - '@', (rl_command_func_t *)clearchan); |
1194 | rl_bind_key ('L'-'@', (rl_command_func_t *) forceredraw); | 1318 | rl_bind_key('L' - '@', (rl_command_func_t *)forceredraw); |
1195 | rl_bind_key ('B'-'@', (rl_command_func_t *) scrollup); | 1319 | rl_bind_key('B' - '@', (rl_command_func_t *)scrollup); |
1196 | rl_bind_key ('P'-'@', (rl_command_func_t *) scrollup); | 1320 | rl_bind_key('P' - '@', (rl_command_func_t *)scrollup); |
1197 | rl_bind_key ('F'-'@', (rl_command_func_t *) scrolldown); | 1321 | rl_bind_key('F' - '@', (rl_command_func_t *)scrolldown); |
1198 | rl_bind_key ('N'-'@', (rl_command_func_t *) scrolldown); | 1322 | rl_bind_key('N' - '@', (rl_command_func_t *)scrolldown); |
1199 | rl_bind_key ('R'-'@', (rl_command_func_t *) scrollwin); | 1323 | rl_bind_key('R' - '@', (rl_command_func_t *)scrollwin); |
1200 | rl_bind_key ('T'-'@', (rl_command_func_t *) shrinkprivwin); | 1324 | rl_bind_key('T' - '@', (rl_command_func_t *)shrinkprivwin); |
1201 | rl_bind_key ('G'-'@', (rl_command_func_t *) growprivwin); | 1325 | rl_bind_key('G' - '@', (rl_command_func_t *)growprivwin); |
1202 | rl_bind_key ('X'-'@', (rl_command_func_t *) toggleprivwin); | 1326 | rl_bind_key('X' - '@', (rl_command_func_t *)toggleprivwin); |
1203 | 1327 | ||
1204 | rl_generic_bind (ISFUNC, "\\M-[5~", (void *)scrollup, keymap); | 1328 | rl_generic_bind(ISFUNC, "\\M-[5~", (void *)scrollup, keymap); |
1205 | rl_generic_bind (ISFUNC, "\\M-[6~", (void *)scrolldown, keymap); | 1329 | rl_generic_bind(ISFUNC, "\\M-[6~", (void *)scrolldown, keymap); |
1206 | 1330 | ||
1207 | /* bind TAB to menu complete from readline */ | 1331 | /* bind TAB to menu complete from readline */ |
1208 | rl_bind_key ('\t', (rl_command_func_t *) rl_menu_complete); | 1332 | rl_bind_key('\t', (rl_command_func_t *)rl_menu_complete); |
1209 | 1333 | ||
1210 | /* application name for .inputrc - err, we don't load it */ | 1334 | /* application name for .inputrc - err, we don't load it */ |
1211 | rl_readline_name = "vchat-client"; | 1335 | rl_readline_name = "vchat-client"; |
1212 | 1336 | ||
1213 | /* set up nick completion functions .. */ | 1337 | /* set up nick completion functions .. */ |
1214 | rl_ignore_completion_duplicates = 0; | 1338 | rl_ignore_completion_duplicates = 0; |
1215 | rl_attempted_completion_function = (rl_completion_func_t *) ul_complete_user; | 1339 | rl_attempted_completion_function = (rl_completion_func_t *)ul_complete_user; |
1216 | 1340 | ||
1217 | /* .. and 'line completed' callback */ | 1341 | /* .. and 'line completed' callback */ |
1218 | rl_callback_handler_install ("", (rl_vcpfunc_t *) linecomplete); | 1342 | rl_callback_handler_install("", (rl_vcpfunc_t *)linecomplete); |
1219 | 1343 | ||
1220 | if( getintoption(CF_PRIVCOLLAPS) ) | 1344 | if (getintoption(CF_PRIVCOLLAPS)) |
1221 | toggleprivwin(); | 1345 | toggleprivwin(); |
1222 | 1346 | ||
1223 | resize(0); | 1347 | resize(0); |
1224 | } | 1348 | } |
1225 | 1349 | ||
1226 | /* render consoleline to screen */ | 1350 | /* render consoleline to screen */ |
1227 | void | 1351 | void consoleline(char *message) { |
1228 | consoleline (char *message) | ||
1229 | { | ||
1230 | /* clear console, set string (or default), redraw display */ | 1352 | /* clear console, set string (or default), redraw display */ |
1231 | int i; | 1353 | int i; |
1232 | ncurs_attr old_att, new_att; | 1354 | ncurs_attr old_att, new_att; |
1233 | 1355 | ||
1234 | togglequery(); | 1356 | togglequery(); |
1235 | 1357 | ||
1236 | memset( &new_att, 0, sizeof(new_att)); | 1358 | memset(&new_att, 0, sizeof(new_att)); |
1237 | BCOLR_SET( (&new_att), 8 ); | 1359 | BCOLR_SET((&new_att), 8); |
1238 | wmove (console, 0, 0); | 1360 | wmove(console, 0, 0); |
1239 | WATTR_GET( console, old_att); | 1361 | WATTR_GET(console, old_att); |
1240 | if(sb_pub->scroll!=sb_pub->count) WATTR_SET( console, new_att); | 1362 | if (sb_pub->scroll != sb_pub->count) |
1363 | WATTR_SET(console, new_att); | ||
1241 | 1364 | ||
1242 | for (i = 0; i < getmaxx(console) - 1; i++) | 1365 | for (i = 0; i < getmaxx(console) - 1; i++) |
1243 | waddch (console, ' '); | 1366 | waddch(console, ' '); |
1244 | 1367 | ||
1245 | if( !message && usetime ) | 1368 | if (!message && usetime) { |
1246 | { | 1369 | char date[10]; |
1247 | char date[10]; | 1370 | time_t now = time(NULL); |
1248 | time_t now = time(NULL); | 1371 | strftime(date, sizeof(date), getformatstr(FS_CONSOLETIME), localtime(&now)); |
1249 | strftime( date, sizeof(date), getformatstr(FS_CONSOLETIME), localtime(&now)); | 1372 | snprintf(tmpstr, TMPSTRSIZE, "%s%s", date, consolestr); |
1250 | snprintf( tmpstr, TMPSTRSIZE, "%s%s", date, consolestr); | 1373 | mvwaddnstr(console, 0, 0, tmpstr, getmaxx(console) - 1); |
1251 | mvwaddnstr (console, 0, 0, tmpstr, getmaxx(console) - 1); | ||
1252 | } else { | 1374 | } else { |
1253 | mvwaddnstr (console, 0, 0, message ? message : consolestr, getmaxx(console) - 1); | 1375 | mvwaddnstr(console, 0, 0, message ? message : consolestr, |
1376 | getmaxx(console) - 1); | ||
1254 | } | 1377 | } |
1255 | 1378 | ||
1256 | snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_SBINF),sb_pub->scroll,sb_pub->count); | 1379 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_SBINF), sb_pub->scroll, |
1257 | mvwaddstr (console, 0, getmaxx(console) - 1 - (strlen(tmpstr)-1),tmpstr); | 1380 | sb_pub->count); |
1258 | if (sb_win == 0) mvwaddch (console, 0, getmaxx(console) - 1,'*'); | 1381 | mvwaddstr(console, 0, getmaxx(console) - 1 - (strlen(tmpstr) - 1), tmpstr); |
1382 | if (sb_win == 0) | ||
1383 | mvwaddch(console, 0, getmaxx(console) - 1, '*'); | ||
1259 | 1384 | ||
1260 | WATTR_SET( console, old_att); | 1385 | WATTR_SET(console, old_att); |
1261 | 1386 | ||
1262 | wnoutrefresh(console); | 1387 | wnoutrefresh(console); |
1263 | if(outputshown) { | 1388 | if (outputshown) { |
1264 | redrawwin(output); | 1389 | redrawwin(output); |
1265 | wnoutrefresh(output); | 1390 | wnoutrefresh(output); |
1266 | } | 1391 | } |
1267 | 1392 | ||
1268 | togglequery(); | 1393 | togglequery(); |
@@ -1271,41 +1396,41 @@ consoleline (char *message) | |||
1271 | } | 1396 | } |
1272 | 1397 | ||
1273 | /* render topicline to screen */ | 1398 | /* render topicline to screen */ |
1274 | void | 1399 | void topicline(char *message) { |
1275 | topicline (char *message) | ||
1276 | { | ||
1277 | int i; | 1400 | int i; |
1278 | ncurs_attr old_att, new_att; | 1401 | ncurs_attr old_att, new_att; |
1279 | 1402 | ||
1280 | if( !topic ) | 1403 | if (!topic) |
1281 | return; | 1404 | return; |
1282 | 1405 | ||
1283 | togglequery(); | 1406 | togglequery(); |
1284 | 1407 | ||
1285 | memset( &new_att, 0, sizeof(new_att)); | 1408 | memset(&new_att, 0, sizeof(new_att)); |
1286 | BCOLR_SET( (&new_att), 8 ); | 1409 | BCOLR_SET((&new_att), 8); |
1287 | 1410 | ||
1288 | /* clear topic, set string (or default), redraw display */ | 1411 | /* clear topic, set string (or default), redraw display */ |
1289 | wmove (topic, 0, 0); | 1412 | wmove(topic, 0, 0); |
1290 | 1413 | ||
1291 | WATTR_GET( topic, old_att); | 1414 | WATTR_GET(topic, old_att); |
1292 | if( private && (sb_priv->scroll!=sb_priv->count)) | 1415 | if (private && (sb_priv->scroll != sb_priv->count)) |
1293 | WATTR_SET( topic, new_att); | 1416 | WATTR_SET(topic, new_att); |
1294 | 1417 | ||
1295 | for (i = 0; i < getmaxx(topic) - 1; i++) | 1418 | for (i = 0; i < getmaxx(topic) - 1; i++) |
1296 | waddch (topic, ' '); | 1419 | waddch(topic, ' '); |
1297 | mvwaddnstr (topic, 0, 0, message ? message : topicstr, getmaxx(topic) - 1); | 1420 | mvwaddnstr(topic, 0, 0, message ? message : topicstr, getmaxx(topic) - 1); |
1298 | if (private) { | 1421 | if (private) { |
1299 | snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_SBINF),sb_priv->scroll,sb_priv->count); | 1422 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_SBINF), sb_priv->scroll, |
1300 | mvwaddstr (topic, 0, getmaxx(topic) - 1 - (strlen(tmpstr)-1),tmpstr); | 1423 | sb_priv->count); |
1301 | if (sb_win == 1) mvwaddch (topic, 0, getmaxx(topic) - 1,'*'); | 1424 | mvwaddstr(topic, 0, getmaxx(topic) - 1 - (strlen(tmpstr) - 1), tmpstr); |
1425 | if (sb_win == 1) | ||
1426 | mvwaddch(topic, 0, getmaxx(topic) - 1, '*'); | ||
1302 | } | 1427 | } |
1303 | WATTR_SET( topic, old_att); | 1428 | WATTR_SET(topic, old_att); |
1304 | 1429 | ||
1305 | wnoutrefresh(topic); | 1430 | wnoutrefresh(topic); |
1306 | if(outputshown) { | 1431 | if (outputshown) { |
1307 | redrawwin(output); | 1432 | redrawwin(output); |
1308 | wnoutrefresh(output); | 1433 | wnoutrefresh(output); |
1309 | } | 1434 | } |
1310 | 1435 | ||
1311 | togglequery(); | 1436 | togglequery(); |
@@ -1314,23 +1439,19 @@ topicline (char *message) | |||
1314 | } | 1439 | } |
1315 | 1440 | ||
1316 | /* end userinterface */ | 1441 | /* end userinterface */ |
1317 | void | 1442 | void exitui(void) { |
1318 | exitui (void) | ||
1319 | { | ||
1320 | if (ui_init) { | 1443 | if (ui_init) { |
1321 | rl_callback_handler_remove (); | 1444 | rl_callback_handler_remove(); |
1322 | endwin (); | 1445 | endwin(); |
1323 | ui_init = 0; | 1446 | ui_init = 0; |
1324 | } | 1447 | } |
1325 | } | 1448 | } |
1326 | 1449 | ||
1327 | /* prompt for a nick */ | 1450 | /* prompt for a nick */ |
1328 | /* FIXME: must not be called when used rl_callback_read_char()/userinput() | 1451 | /* FIXME: must not be called when used rl_callback_read_char()/userinput() |
1329 | * before */ | 1452 | * before */ |
1330 | void | 1453 | void nickprompt(void) { |
1331 | nickprompt (void) | 1454 | char *newnick = 0; |
1332 | { | ||
1333 | char * newnick = 0; | ||
1334 | 1455 | ||
1335 | if (own_nick_get()) | 1456 | if (own_nick_get()) |
1336 | return; | 1457 | return; |
@@ -1341,15 +1462,15 @@ nickprompt (void) | |||
1341 | newnick = readline(""); | 1462 | newnick = readline(""); |
1342 | 1463 | ||
1343 | own_nick_set(newnick); | 1464 | own_nick_set(newnick); |
1344 | setstroption(CF_NICK,newnick); | 1465 | setstroption(CF_NICK, newnick); |
1345 | 1466 | ||
1346 | /* try to get readlines stats clean again */ | 1467 | /* try to get readlines stats clean again */ |
1347 | //rl_free_line_state (); | 1468 | // rl_free_line_state (); |
1348 | memset( rl_line_buffer, 0, rl_end ); | 1469 | memset(rl_line_buffer, 0, rl_end); |
1349 | rl_point = rl_end = rl_done = 0; | 1470 | rl_point = rl_end = rl_done = 0; |
1350 | 1471 | ||
1351 | /* wipe input line and reset cursor */ | 1472 | /* wipe input line and reset cursor */ |
1352 | rl_kill_full_line(0,0); | 1473 | rl_kill_full_line(0, 0); |
1353 | wclear(input); | 1474 | wclear(input); |
1354 | 1475 | ||
1355 | /* reset consoleline */ | 1476 | /* reset consoleline */ |
@@ -1357,20 +1478,18 @@ nickprompt (void) | |||
1357 | } | 1478 | } |
1358 | 1479 | ||
1359 | /* special callback for readline, doesn't show the characters */ | 1480 | /* special callback for readline, doesn't show the characters */ |
1360 | static void | 1481 | static void vcnredraw(void) { |
1361 | vcnredraw (void) | ||
1362 | { | ||
1363 | int i; | 1482 | int i; |
1364 | char *passbof="-*-*-*-*-*-*-"; | 1483 | char *passbof = "-*-*-*-*-*-*-"; |
1365 | 1484 | ||
1366 | /* wipe input line and reset cursor */ | 1485 | /* wipe input line and reset cursor */ |
1367 | wmove(input, 0, 0); | 1486 | wmove(input, 0, 0); |
1368 | for (i = 0; i < getmaxx(input) - 1; i++) | 1487 | for (i = 0; i < getmaxx(input) - 1; i++) |
1369 | waddch(input, ' '); | 1488 | waddch(input, ' '); |
1370 | wmove(input, 0, 0); | 1489 | wmove(input, 0, 0); |
1371 | 1490 | ||
1372 | /* draw as many stars as there are characters */ | 1491 | /* draw as many stars as there are characters */ |
1373 | mvwaddnstr(input, 0, 0, &passbof[rl_point%2], 12); | 1492 | mvwaddnstr(input, 0, 0, &passbof[rl_point % 2], 12); |
1374 | wmove(input, 0, getmaxx(input) - 1); | 1493 | wmove(input, 0, getmaxx(input) - 1); |
1375 | wrefresh(input); | 1494 | wrefresh(input); |
1376 | } | 1495 | } |
@@ -1378,11 +1497,11 @@ vcnredraw (void) | |||
1378 | /* passphrase callback for OpenSSL */ | 1497 | /* passphrase callback for OpenSSL */ |
1379 | /* FIXME: must not be called when used rl_callback_read_char()/userinput() | 1498 | /* FIXME: must not be called when used rl_callback_read_char()/userinput() |
1380 | * before */ | 1499 | * before */ |
1381 | int | 1500 | int passprompt(char *buf, int size, int rwflag, void *userdata) { |
1382 | passprompt (char *buf, int size, int rwflag, void *userdata) | ||
1383 | { | ||
1384 | int i; | 1501 | int i; |
1385 | char *passphrase = NULL; | 1502 | char *passphrase = NULL; |
1503 | (void)rwflag; | ||
1504 | (void)userdata; | ||
1386 | 1505 | ||
1387 | /* use special non-revealing redraw function */ | 1506 | /* use special non-revealing redraw function */ |
1388 | /* FIXME: passphrase isn't protected against e.g. swapping */ | 1507 | /* FIXME: passphrase isn't protected against e.g. swapping */ |
@@ -1390,45 +1509,43 @@ passprompt (char *buf, int size, int rwflag, void *userdata) | |||
1390 | 1509 | ||
1391 | /* prompt user for non-empty passphrase */ | 1510 | /* prompt user for non-empty passphrase */ |
1392 | consoleline("Please enter PEM passphrase for private key:"); | 1511 | consoleline("Please enter PEM passphrase for private key:"); |
1393 | while (!passphrase || !passphrase[0]) | 1512 | while (!passphrase || !passphrase[0]) { |
1394 | { | 1513 | if (passphrase) |
1395 | if (passphrase) | 1514 | free(passphrase); |
1396 | free (passphrase); | 1515 | passphrase = readline(""); |
1397 | passphrase = readline (""); | 1516 | } |
1398 | } | ||
1399 | 1517 | ||
1400 | /* reset redrawing function to default, reset consoleline */ | 1518 | /* reset redrawing function to default, reset consoleline */ |
1401 | rl_redisplay_function = vciredraw; | 1519 | rl_redisplay_function = vciredraw; |
1402 | consoleline(NULL); | 1520 | consoleline(NULL); |
1403 | 1521 | ||
1404 | /* copy passphrase to buffer */ | 1522 | /* copy passphrase to buffer */ |
1405 | strncpy (buf, passphrase, size); | 1523 | strncpy(buf, passphrase, size); |
1406 | 1524 | ||
1407 | /* try to get readlines stats clean again */ | 1525 | /* try to get readlines stats clean again */ |
1408 | //rl_free_line_state (); | 1526 | // rl_free_line_state (); |
1409 | memset( rl_line_buffer, 0, rl_end ); | 1527 | memset(rl_line_buffer, 0, rl_end); |
1410 | rl_point = rl_end = rl_done = 0; | 1528 | rl_point = rl_end = rl_done = 0; |
1411 | 1529 | ||
1412 | /* wipe input line and reset cursor */ | 1530 | /* wipe input line and reset cursor */ |
1413 | wmove (input, 0, 0); | 1531 | wmove(input, 0, 0); |
1414 | for (i = 0; i < getmaxx(input) - 1; i++) | 1532 | for (i = 0; i < getmaxx(input) - 1; i++) |
1415 | waddch (input, ' '); | 1533 | waddch(input, ' '); |
1416 | wmove (input, 0, 0); | 1534 | wmove(input, 0, 0); |
1417 | wrefresh (input); | 1535 | wrefresh(input); |
1418 | 1536 | ||
1419 | /* return passphrase to OpenSSL */ | 1537 | /* return passphrase to OpenSSL */ |
1420 | return strlen (buf); | 1538 | return strlen(buf); |
1421 | } | 1539 | } |
1422 | 1540 | ||
1423 | /* Filter stuff */ | 1541 | /* Filter stuff */ |
1424 | static int | 1542 | static int check_valid_colour(char colour) { |
1425 | check_valid_colour( char colour ) { | 1543 | return !((colour != '-') && (colour != '+') && |
1426 | return !( (colour !='-')&&(colour !='+') && (colour < '0' || colour > '9') && | 1544 | (colour < '0' || colour > '9') && |
1427 | (colour < 'A' || colour > 'Z' || !attributes[ colour-'A' ]) && | 1545 | (colour < 'A' || colour > 'Z' || !attributes[colour - 'A']) && |
1428 | (colour < 'a' || colour > 'z' || !attributes[ colour-'a' ])); | 1546 | (colour < 'a' || colour > 'z' || !attributes[colour - 'a'])); |
1429 | } | 1547 | } |
1430 | 1548 | ||
1431 | |||
1432 | /* scans filterlist and removes possible matches | 1549 | /* scans filterlist and removes possible matches |
1433 | test functions may return: | 1550 | test functions may return: |
1434 | RMFILTER_RMANDCONT | 1551 | RMFILTER_RMANDCONT |
@@ -1437,101 +1554,101 @@ check_valid_colour( char colour ) { | |||
1437 | RMFILTER_KEEPANDSTOP | 1554 | RMFILTER_KEEPANDSTOP |
1438 | returns number of removed entries | 1555 | returns number of removed entries |
1439 | */ | 1556 | */ |
1440 | static int | 1557 | static int removefromfilterlist(int (*test)(filt *flt, void *data, char colour), |
1441 | removefromfilterlist( int(*test)(filt *flt, void *data, char colour), void *data, char colour) { | 1558 | void *data, char colour) { |
1442 | filt **flt = &filterlist, *tmp; | 1559 | filt **flt = &filterlist, *tmp; |
1443 | int removed = 0, stop = 0; | 1560 | int removed = 0, stop = 0; |
1444 | 1561 | ||
1445 | while( *flt && !stop ) { | 1562 | while (*flt && !stop) { |
1446 | switch( test( *flt, data, colour ) ) { | 1563 | switch (test(*flt, data, colour)) { |
1447 | case RMFILTER_RMANDSTOP: /* remove */ | 1564 | case RMFILTER_RMANDSTOP: /* remove */ |
1448 | stop = 1; | 1565 | stop = 1; |
1449 | case RMFILTER_RMANDCONT: | 1566 | case RMFILTER_RMANDCONT: |
1450 | snprintf( tmpstr, TMPSTRSIZE, " Removed ID: [% 3d] Color: [%c] Regex: [%s] ", (*flt)->id, (*flt)->colour, (*flt)->text); | 1567 | snprintf(tmpstr, TMPSTRSIZE, |
1451 | writeout(tmpstr); | 1568 | " Removed ID: [% 3d] Color: [%c] Regex: [%s] ", (*flt)->id, |
1452 | /* Release regex.h resources */ | 1569 | (*flt)->colour, (*flt)->text); |
1453 | regfree( &((*flt)->regex)); | 1570 | writeout(tmpstr); |
1454 | /* free ASCII text memory */ | 1571 | /* Release regex.h resources */ |
1455 | free( (*flt)->text); | 1572 | regfree(&((*flt)->regex)); |
1456 | /* unlink from list */ | 1573 | /* free ASCII text memory */ |
1457 | tmp = *flt; | 1574 | free((*flt)->text); |
1458 | *flt = (*flt)->next; | 1575 | /* unlink from list */ |
1459 | /* free filter block itself */ | 1576 | tmp = *flt; |
1460 | free( tmp ); | 1577 | *flt = (*flt)->next; |
1461 | /* reflect changes on whole screen */ | 1578 | /* free filter block itself */ |
1462 | removed++; | 1579 | free(tmp); |
1463 | break; | 1580 | /* reflect changes on whole screen */ |
1464 | case RMFILTER_KEEPANDSTOP: /* don't remove but stop scanning */ | 1581 | removed++; |
1465 | stop = 1; | 1582 | break; |
1466 | break; | 1583 | case RMFILTER_KEEPANDSTOP: /* don't remove but stop scanning */ |
1467 | default: | 1584 | stop = 1; |
1468 | /* advance in list */ | 1585 | break; |
1469 | if( *flt ) flt = &((*flt)->next); | 1586 | default: |
1470 | break; | 1587 | /* advance in list */ |
1471 | } | 1588 | if (*flt) |
1589 | flt = &((*flt)->next); | ||
1590 | break; | ||
1591 | } | ||
1472 | } | 1592 | } |
1473 | /* return number of removed items */ | 1593 | /* return number of removed items */ |
1474 | return removed; | 1594 | return removed; |
1475 | } | 1595 | } |
1476 | 1596 | ||
1477 | static int | 1597 | static int test_clear(filt *flt, void *data, char c) { |
1478 | test_clear( filt *flt, void *data, char c ) { | 1598 | (void)data; |
1479 | if( !c || ( c == flt->colour ) || ( (c == '*') && (flt->colour != '-') && (flt->colour != '+') ) ) | 1599 | if (!c || (c == flt->colour) || |
1480 | return RMFILTER_RMANDCONT; | 1600 | ((c == '*') && (flt->colour != '-') && (flt->colour != '+'))) |
1601 | return RMFILTER_RMANDCONT; | ||
1481 | else | 1602 | else |
1482 | return RMFILTER_KEEPANDCONT; | 1603 | return RMFILTER_KEEPANDCONT; |
1483 | } | 1604 | } |
1484 | 1605 | ||
1485 | static int | 1606 | static int test_simplerm(filt *flt, void *data, char colour) { |
1486 | test_simplerm( filt *flt, void *data, char colour) { | 1607 | if (!strcmp(flt->text, (char *)data)) |
1487 | if( !strcmp( flt->text, (char*)data)) | 1608 | return test_clear(flt, NULL, colour); |
1488 | return test_clear(flt, NULL, colour); | ||
1489 | else | 1609 | else |
1490 | return RMFILTER_KEEPANDCONT; | 1610 | return RMFILTER_KEEPANDCONT; |
1491 | } | 1611 | } |
1492 | 1612 | ||
1493 | static int | 1613 | static int test_numericrm(filt *flt, void *data, char colour) { |
1494 | test_numericrm( filt *flt, void *data, char colour) { | 1614 | if (flt->id == (long)data) |
1495 | if( flt->id == (long)data) | 1615 | return test_clear(flt, NULL, colour); |
1496 | return test_clear(flt, NULL, colour); | ||
1497 | else | 1616 | else |
1498 | return RMFILTER_KEEPANDCONT; | 1617 | return RMFILTER_KEEPANDCONT; |
1499 | } | 1618 | } |
1500 | 1619 | ||
1501 | /* clears filter list */ | 1620 | /* clears filter list */ |
1502 | void | 1621 | void clearfilters(char colour) { |
1503 | clearfilters( char colour ) { | 1622 | flushout(); |
1504 | flushout( ); | 1623 | if (removefromfilterlist(test_clear, NULL, colour)) { |
1505 | if( removefromfilterlist( test_clear, NULL, colour ) ) { | 1624 | /* There actually WERE items removed */ |
1506 | /* There actually WERE items removed */ | 1625 | filtertype = analyzefilters(); |
1507 | filtertype = analyzefilters( ); | ||
1508 | } else { | 1626 | } else { |
1509 | writeout(" No matches on filter list. "); | 1627 | writeout(" No matches on filter list. "); |
1510 | } | 1628 | } |
1511 | showout(); | 1629 | showout(); |
1512 | } | 1630 | } |
1513 | 1631 | ||
1514 | /* removes filter pattern */ | 1632 | /* removes filter pattern */ |
1515 | void | 1633 | void removefilter(char *tail) { |
1516 | removefilter( char *tail ) { | ||
1517 | int rmv = 0, val; | 1634 | int rmv = 0, val; |
1518 | char* end; | 1635 | char *end; |
1519 | 1636 | ||
1520 | flushout( ); | 1637 | flushout(); |
1521 | 1638 | ||
1522 | rmv = removefromfilterlist( test_simplerm, (void *)tail, 0 ); | 1639 | rmv = removefromfilterlist(test_simplerm, (void *)tail, 0); |
1523 | if(!rmv) { | 1640 | if (!rmv) { |
1524 | val = strtol(tail, &end, 10); | 1641 | val = strtol(tail, &end, 10); |
1525 | if( (tail != end) && (!*end) ) | 1642 | if ((tail != end) && (!*end)) |
1526 | rmv = removefromfilterlist( test_numericrm, (void *)(uintptr_t)val, 0); | 1643 | rmv = removefromfilterlist(test_numericrm, (void *)(uintptr_t)val, 0); |
1527 | } | 1644 | } |
1528 | 1645 | ||
1529 | if( rmv ) { | 1646 | if (rmv) { |
1530 | /* There actually WERE items removed */ | 1647 | /* There actually WERE items removed */ |
1531 | filtertype = analyzefilters( ); | 1648 | filtertype = analyzefilters(); |
1532 | } else { | 1649 | } else { |
1533 | snprintf( tmpstr, TMPSTRSIZE, " Not on filter list: %s ", tail); | 1650 | snprintf(tmpstr, TMPSTRSIZE, " Not on filter list: %s ", tail); |
1534 | writeout( tmpstr ); | 1651 | writeout(tmpstr); |
1535 | } | 1652 | } |
1536 | showout(); | 1653 | showout(); |
1537 | } | 1654 | } |
@@ -1539,139 +1656,158 @@ removefilter( char *tail ) { | |||
1539 | static unsigned int uniqueidpool = 1; | 1656 | static unsigned int uniqueidpool = 1; |
1540 | 1657 | ||
1541 | /* returns unique id for filter pattern or 0 for failure */ | 1658 | /* returns unique id for filter pattern or 0 for failure */ |
1542 | unsigned int | 1659 | unsigned int addfilter(char colour, char *regex) { |
1543 | addfilter( char colour, char *regex ) { | 1660 | filt *newflt = malloc(sizeof(filt)), **flt = &filterlist; |
1544 | filt *newflt = malloc( sizeof(filt)), **flt = &filterlist; | ||
1545 | 1661 | ||
1546 | if( !newflt ) return 0; | 1662 | if (!newflt) |
1547 | flushout( ); | 1663 | return 0; |
1664 | flushout(); | ||
1548 | 1665 | ||
1549 | /* check colour validity */ | 1666 | /* check colour validity */ |
1550 | if( !check_valid_colour( colour ) ){ | 1667 | if (!check_valid_colour(colour)) { |
1551 | free( newflt ); | 1668 | free(newflt); |
1552 | writeout( " Not a valid colour code. " ); | 1669 | writeout(" Not a valid colour code. "); |
1553 | showout( ); | 1670 | showout(); |
1554 | return 0; | 1671 | return 0; |
1555 | } | 1672 | } |
1556 | 1673 | ||
1557 | if( regcomp( &newflt->regex, regex, REG_ICASE | REG_EXTENDED | REG_NEWLINE) ) { | 1674 | if (regcomp(&newflt->regex, regex, REG_ICASE | REG_EXTENDED | REG_NEWLINE)) { |
1558 | /* couldn't compile regex ... print error, return */ | 1675 | /* couldn't compile regex ... print error, return */ |
1559 | free( newflt ); | 1676 | free(newflt); |
1560 | 1677 | ||
1561 | snprintf( tmpstr, TMPSTRSIZE, " %s ", regex); | 1678 | snprintf(tmpstr, TMPSTRSIZE, " %s ", regex); |
1562 | writeout( " Bad regular expression: "); | 1679 | writeout(" Bad regular expression: "); |
1563 | writeout( tmpstr ); | 1680 | writeout(tmpstr); |
1564 | showout( ); | 1681 | showout(); |
1565 | return 0; | 1682 | return 0; |
1566 | } else { | 1683 | } else { |
1567 | int len = strlen(regex) + 1; | 1684 | int len = strlen(regex) + 1; |
1568 | /* grab id from ID pool an increase free ID counter */ | 1685 | /* grab id from ID pool an increase free ID counter */ |
1569 | newflt->id = uniqueidpool++; | 1686 | newflt->id = uniqueidpool++; |
1570 | newflt->colour = colour; | 1687 | newflt->colour = colour; |
1571 | newflt->next = NULL; | 1688 | newflt->next = NULL; |
1572 | /* take a copy of plain regex text for later identification by user */ | 1689 | /* take a copy of plain regex text for later identification by user */ |
1573 | newflt->text = malloc( len ); | 1690 | newflt->text = malloc(len); |
1574 | memcpy( newflt->text, regex, len ); | 1691 | memcpy(newflt->text, regex, len); |
1575 | } | 1692 | } |
1576 | 1693 | ||
1577 | /* append new filter to filterlist */ | 1694 | /* append new filter to filterlist */ |
1578 | while( *flt ) flt=&((*flt)->next); | 1695 | while (*flt) |
1696 | flt = &((*flt)->next); | ||
1579 | *flt = newflt; | 1697 | *flt = newflt; |
1580 | 1698 | ||
1581 | filtertype = analyzefilters( ); | 1699 | filtertype = analyzefilters(); |
1582 | 1700 | ||
1583 | if ( colour == '-' ) { | 1701 | if (colour == '-') { |
1584 | snprintf( tmpstr, TMPSTRSIZE, " \"%s\" successfully added to ignorance list. ( ID = %d). ", (*flt)->text, (*flt)->id); | 1702 | snprintf(tmpstr, TMPSTRSIZE, |
1585 | } else if( colour == '+' ) { | 1703 | " \"%s\" successfully added to ignorance list. ( ID = %d). ", |
1586 | snprintf( tmpstr, TMPSTRSIZE, " \"%s\" successfully added to zoom list. ( ID = %d). ", (*flt)->text, (*flt)->id); | 1704 | (*flt)->text, (*flt)->id); |
1705 | } else if (colour == '+') { | ||
1706 | snprintf(tmpstr, TMPSTRSIZE, | ||
1707 | " \"%s\" successfully added to zoom list. ( ID = %d). ", | ||
1708 | (*flt)->text, (*flt)->id); | ||
1587 | } else { | 1709 | } else { |
1588 | snprintf( tmpstr, TMPSTRSIZE, " \"%s\" successfully added to hilitelist. (ID = %d). ", (*flt)->text, (*flt)->id); | 1710 | snprintf(tmpstr, TMPSTRSIZE, |
1711 | " \"%s\" successfully added to hilitelist. (ID = %d). ", | ||
1712 | (*flt)->text, (*flt)->id); | ||
1589 | } | 1713 | } |
1590 | writeout(tmpstr ); | 1714 | writeout(tmpstr); |
1591 | showout( ); | 1715 | showout(); |
1592 | 1716 | ||
1593 | return newflt->id; | 1717 | return newflt->id; |
1594 | } | 1718 | } |
1595 | 1719 | ||
1596 | void | 1720 | void listfilters(void) { |
1597 | listfilters( void ) { | 1721 | filt *flt = filterlist; |
1598 | filt *flt = filterlist; | 1722 | int shownhi = 0, shownign = 0, shownzoom = 0; |
1599 | int shownhi = 0, shownign = 0, shownzoom = 0; | ||
1600 | 1723 | ||
1601 | flushout( ); | 1724 | flushout(); |
1602 | 1725 | ||
1603 | while( flt ) { | 1726 | while (flt) { |
1604 | if( (flt->colour != '-') && (flt->colour != '+')) { | 1727 | if ((flt->colour != '-') && (flt->colour != '+')) { |
1605 | if(!shownhi) { | 1728 | if (!shownhi) { |
1606 | writeout(" Your hilites:"); | 1729 | writeout(" Your hilites:"); |
1607 | shownhi = 1; | 1730 | shownhi = 1; |
1608 | } | ||
1609 | snprintf( tmpstr, TMPSTRSIZE, " ID: [% 3d] Color: [%c] Regex: [%s]", flt->id, flt->colour, flt->text); | ||
1610 | writeout( tmpstr ); | ||
1611 | } | 1731 | } |
1612 | flt = flt->next; | 1732 | snprintf(tmpstr, TMPSTRSIZE, " ID: [% 3d] Color: [%c] Regex: [%s]", |
1733 | flt->id, flt->colour, flt->text); | ||
1734 | writeout(tmpstr); | ||
1735 | } | ||
1736 | flt = flt->next; | ||
1613 | } | 1737 | } |
1614 | 1738 | ||
1615 | flt = filterlist; | 1739 | flt = filterlist; |
1616 | 1740 | ||
1617 | while( flt ) { | 1741 | while (flt) { |
1618 | if( flt->colour == '-') { | 1742 | if (flt->colour == '-') { |
1619 | if(!shownign) { | 1743 | if (!shownign) { |
1620 | if(shownhi) writeout(" "); | 1744 | if (shownhi) |
1621 | writeout(" You do ignore:"); | 1745 | writeout(" "); |
1622 | shownign = 1; | 1746 | writeout(" You do ignore:"); |
1623 | } | 1747 | shownign = 1; |
1624 | snprintf( tmpstr, TMPSTRSIZE, " ID: [% 3d] Regex: [%s]", flt->id, flt->text); | ||
1625 | writeout( tmpstr ); | ||
1626 | } | 1748 | } |
1627 | flt = flt->next; | 1749 | snprintf(tmpstr, TMPSTRSIZE, " ID: [% 3d] Regex: [%s]", |
1750 | flt->id, flt->text); | ||
1751 | writeout(tmpstr); | ||
1752 | } | ||
1753 | flt = flt->next; | ||
1628 | } | 1754 | } |
1629 | 1755 | ||
1630 | flt = filterlist; | 1756 | flt = filterlist; |
1631 | 1757 | ||
1632 | while( flt ) { | 1758 | while (flt) { |
1633 | if( flt->colour == '+') { | 1759 | if (flt->colour == '+') { |
1634 | if(!shownzoom) { | 1760 | if (!shownzoom) { |
1635 | if(shownhi || shownign) writeout(" "); | 1761 | if (shownhi || shownign) |
1636 | writeout(" On your whitelist:"); | 1762 | writeout(" "); |
1637 | shownzoom = 1; | 1763 | writeout(" On your whitelist:"); |
1638 | } | 1764 | shownzoom = 1; |
1639 | snprintf( tmpstr, TMPSTRSIZE, " ID: [% 3d] Regex: [%s]", flt->id, flt->text); | ||
1640 | writeout( tmpstr ); | ||
1641 | } | 1765 | } |
1642 | flt = flt->next; | 1766 | snprintf(tmpstr, TMPSTRSIZE, " ID: [% 3d] Regex: [%s]", |
1767 | flt->id, flt->text); | ||
1768 | writeout(tmpstr); | ||
1769 | } | ||
1770 | flt = flt->next; | ||
1643 | } | 1771 | } |
1644 | 1772 | ||
1645 | if( !shownign && !shownhi && !shownzoom) { | 1773 | if (!shownign && !shownhi && !shownzoom) { |
1646 | writeout(" No entries on your filter list. "); | 1774 | writeout(" No entries on your filter list. "); |
1647 | } | 1775 | } |
1648 | showout(); | 1776 | showout(); |
1649 | } | 1777 | } |
1650 | 1778 | ||
1651 | void | 1779 | void handlequery(char *tail) { |
1652 | handlequery( char *tail ) { | 1780 | if (*tail) { |
1653 | if( *tail ) { | ||
1654 | // ".m %s " -> string + 4 | 1781 | // ".m %s " -> string + 4 |
1655 | if( querypartner && private ) { | 1782 | if (querypartner && private) { |
1656 | WINDOW *tmp= private; private = channel; channel = tmp; | 1783 | WINDOW *tmp = private; |
1784 | private | ||
1785 | = channel; | ||
1786 | channel = tmp; | ||
1657 | } | 1787 | } |
1658 | querypartner = (char *)realloc( querypartner, 5 + strlen( tail )); | 1788 | querypartner = (char *)realloc(querypartner, 5 + strlen(tail)); |
1659 | if( querypartner ) { | 1789 | if (querypartner) { |
1660 | snprintf( querypartner, 5 + strlen( tail ), ".m %s ", tail ); | 1790 | snprintf(querypartner, 5 + strlen(tail), ".m %s ", tail); |
1661 | if( private ) { | 1791 | if (private) { |
1662 | WINDOW *tmp= private; private = channel; channel = tmp; | 1792 | WINDOW *tmp = private; |
1793 | private | ||
1794 | = channel; | ||
1795 | channel = tmp; | ||
1663 | } | 1796 | } |
1664 | } | 1797 | } |
1665 | resize( 0 ); | 1798 | resize(0); |
1666 | } else { | 1799 | } else { |
1667 | // QUERY ends | 1800 | // QUERY ends |
1668 | if( querypartner ) { | 1801 | if (querypartner) { |
1669 | free( querypartner ); | 1802 | free(querypartner); |
1670 | querypartner = NULL; | 1803 | querypartner = NULL; |
1671 | if( private ) { | 1804 | if (private) { |
1672 | WINDOW *tmp= private; private = channel; channel = tmp; | 1805 | WINDOW *tmp = private; |
1806 | private | ||
1807 | = channel; | ||
1808 | channel = tmp; | ||
1673 | } | 1809 | } |
1674 | resize( 0 ); | 1810 | resize(0); |
1675 | } | 1811 | } |
1676 | } | 1812 | } |
1677 | } | 1813 | } |
diff --git a/vchat-user.c b/vchat-user.c index 881a3cf..1ab2048 100755..100644 --- a/vchat-user.c +++ b/vchat-user.c | |||
@@ -3,42 +3,48 @@ | |||
3 | 3 | ||
4 | */ | 4 | */ |
5 | 5 | ||
6 | #include <regex.h> | ||
6 | #include <stdint.h> | 7 | #include <stdint.h> |
8 | #include <stdio.h> | ||
7 | #include <stdlib.h> | 9 | #include <stdlib.h> |
8 | #include <strings.h> | 10 | #include <strings.h> |
9 | #include <stdio.h> | ||
10 | #include <sys/time.h> | 11 | #include <sys/time.h> |
11 | #include <regex.h> | 12 | |
12 | #include <readline/readline.h> | 13 | #include <readline/readline.h> |
13 | 14 | ||
14 | #include "vchat.h" | ||
15 | #include "vchat-user.h" | 15 | #include "vchat-user.h" |
16 | #include "vchat.h" | ||
16 | 17 | ||
17 | /* version of this module */ | 18 | /* version of this module */ |
18 | char *vchat_us_version = "vchat-user.c $Id$"; | 19 | char *vchat_us_version = |
19 | 20 | "vchat-user.c $Id$"; | |
20 | typedef struct | 21 | |
21 | { | 22 | typedef struct { |
22 | char *nick; | 23 | char *nick; |
23 | enum { UL_NONE = 0x00, UL_ME = 0x01, UL_IN_MY_CHAN = 0x02, UL_NOT_IN_LIST = 0x04 } flags; | 24 | enum { |
25 | UL_NONE = 0x00, | ||
26 | UL_ME = 0x01, | ||
27 | UL_IN_MY_CHAN = 0x02, | ||
28 | UL_NOT_IN_LIST = 0x04 | ||
29 | } flags; | ||
24 | uint64_t last_public; | 30 | uint64_t last_public; |
25 | uint64_t last_private; | 31 | uint64_t last_private; |
26 | } user; | 32 | } user; |
27 | static user *g_users; //< all users, incl self | 33 | static user *g_users; //< all users, incl self |
28 | static size_t g_users_count; //< number of users in list | 34 | static size_t g_users_count; //< number of users in list |
29 | static char *g_nick; //< own nick | 35 | static char *g_nick; //< own nick |
30 | static int g_channel; //< own channel | 36 | static int g_channel; //< own channel |
31 | unsigned int ul_case_first = 0; | 37 | unsigned int ul_case_first = 0; |
32 | 38 | ||
33 | static char **g_dict; | 39 | static char **g_dict; |
34 | static size_t g_dict_len; | 40 | static size_t g_dict_len; |
35 | 41 | ||
36 | static int ul_nick_lookup( const char *nick, int *exact_match ) { | 42 | static int ul_nick_lookup(const char *nick, int *exact_match) { |
37 | int i; | 43 | int i; |
38 | 44 | ||
39 | *exact_match = 1; | 45 | *exact_match = 1; |
40 | for( i=0; i<g_users_count; ++i ) | 46 | for (i = 0; i < g_users_count; ++i) |
41 | if( !strcasecmp( g_users[i].nick, nick ) ) | 47 | if (!strcasecmp(g_users[i].nick, nick)) |
42 | return i; | 48 | return i; |
43 | *exact_match = 0; | 49 | *exact_match = 0; |
44 | return i; | 50 | return i; |
@@ -46,78 +52,83 @@ static int ul_nick_lookup( const char *nick, int *exact_match ) { | |||
46 | 52 | ||
47 | static int64_t ul_now() { | 53 | static int64_t ul_now() { |
48 | struct timeval now; | 54 | struct timeval now; |
49 | gettimeofday(&now,(struct timezone*) 0); | 55 | gettimeofday(&now, (struct timezone *)0); |
50 | return ((uint64_t)now.tv_sec * 1000) + ((uint64_t)now.tv_usec / 1000 ); | 56 | return ((uint64_t)now.tv_sec * 1000) + ((uint64_t)now.tv_usec / 1000); |
51 | } | 57 | } |
52 | 58 | ||
53 | /* own nick and channel setters/getters */ | 59 | /* own nick and channel setters/getters */ |
54 | void own_nick_set( char *nick ) { | 60 | void own_nick_set(char *nick) { |
55 | if( nick ) { | 61 | if (nick) { |
56 | int base; | 62 | int base; |
57 | if( g_nick ) | 63 | if (g_nick) |
58 | base = ul_rename( g_nick, nick ); | 64 | base = ul_rename(g_nick, nick); |
59 | else | 65 | else |
60 | base = ul_add( nick, 0 ); | 66 | base = ul_add(nick, 0); |
61 | if( base >= 0 ) { | 67 | if (base >= 0) { |
62 | g_users[base].flags |= UL_ME; | 68 | g_users[base].flags |= UL_ME; |
63 | g_nick = g_users[base].nick; | 69 | g_nick = g_users[base].nick; |
64 | } | 70 | } |
65 | } else | 71 | } else |
66 | ul_del( g_nick ); | 72 | ul_del(g_nick); |
67 | 73 | ||
68 | setstroption(CF_NICK, nick); | 74 | setstroption(CF_NICK, nick); |
69 | } | 75 | } |
70 | 76 | ||
71 | void own_channel_set( int channel ) { | 77 | void own_channel_set(int channel) { |
72 | if( channel != g_channel ) { | 78 | if (channel != g_channel) { |
73 | /* Remove all users from my chan, will be re-set on join message */ | 79 | /* Remove all users from my chan, will be re-set on join message */ |
74 | int i; | 80 | int i; |
75 | for( i=0; i<g_users_count; ++i ) | 81 | for (i = 0; i < g_users_count; ++i) |
76 | g_users[i].flags &= ~UL_IN_MY_CHAN; | 82 | g_users[i].flags &= ~UL_IN_MY_CHAN; |
77 | } | 83 | } |
78 | 84 | ||
79 | g_channel = channel; | 85 | g_channel = channel; |
80 | } | 86 | } |
81 | 87 | ||
82 | char const *own_nick_get( ) { | 88 | char const *own_nick_get() { return g_nick; } |
83 | return g_nick; | ||
84 | } | ||
85 | 89 | ||
86 | int own_nick_check( char *nick ) { | 90 | int own_nick_check(char *nick) { |
87 | if( !g_nick ) return -1; | 91 | if (!g_nick) |
88 | return !strcasecmp(g_nick,nick); | 92 | return -1; |
93 | return !strcasecmp(g_nick, nick); | ||
89 | } | 94 | } |
90 | 95 | ||
91 | int own_channel_get( ) { | 96 | int own_channel_get() { return g_channel; } |
92 | return g_channel; | ||
93 | } | ||
94 | 97 | ||
95 | /* Add/remove/rename */ | 98 | /* Add/remove/rename */ |
96 | int ul_add(char *name, int in_my_chan_flag ) { | 99 | int ul_add(char *name, int in_my_chan_flag) { |
97 | 100 | ||
98 | /* Test if user is already known */ | 101 | /* Test if user is already known */ |
99 | int exact_match, base = ul_nick_lookup( name, &exact_match ); | 102 | int exact_match, base = ul_nick_lookup(name, &exact_match); |
100 | if( !exact_match ) { | 103 | if (!exact_match) { |
101 | /* Make space for new user */ | 104 | /* Make space for new user */ |
102 | user * new_users = realloc( g_users, sizeof( user ) * ( 1 + g_users_count ) ); | 105 | user *new_users = realloc(g_users, sizeof(user) * (1 + g_users_count)); |
103 | if( !new_users ) return -1; | 106 | if (!new_users) |
107 | return -1; | ||
104 | 108 | ||
105 | /* Copy the tail */ | 109 | /* Copy the tail */ |
106 | g_users = new_users; | 110 | g_users = new_users; |
107 | memmove( g_users + base + 1, g_users + base, ( g_users_count - base ) * sizeof( user ) ); | 111 | memmove(g_users + base + 1, g_users + base, |
108 | g_users[base].nick = strdup( name ); | 112 | (g_users_count - base) * sizeof(user)); |
109 | g_users[base].flags = UL_NONE; | 113 | g_users[base].nick = strdup(name); |
110 | g_users[base].last_public = 0; | 114 | g_users[base].flags = UL_NONE; |
115 | g_users[base].last_public = 0; | ||
111 | g_users[base].last_private = 0; | 116 | g_users[base].last_private = 0; |
112 | 117 | ||
113 | g_users_count++; | 118 | g_users_count++; |
114 | } | 119 | } |
115 | 120 | ||
116 | g_users[base].flags &= ~UL_NOT_IN_LIST; | 121 | g_users[base].flags &= ~UL_NOT_IN_LIST; |
117 | switch( in_my_chan_flag ) { | 122 | switch (in_my_chan_flag) { |
118 | case 1: g_users[base].flags |= UL_IN_MY_CHAN; break; | 123 | case 1: |
119 | case 0: g_users[base].flags &= ~UL_IN_MY_CHAN; break; | 124 | g_users[base].flags |= UL_IN_MY_CHAN; |
120 | case -1: default: break; | 125 | break; |
126 | case 0: | ||
127 | g_users[base].flags &= ~UL_IN_MY_CHAN; | ||
128 | break; | ||
129 | case -1: | ||
130 | default: | ||
131 | break; | ||
121 | } | 132 | } |
122 | 133 | ||
123 | return base; | 134 | return base; |
@@ -125,30 +136,33 @@ int ul_add(char *name, int in_my_chan_flag ) { | |||
125 | 136 | ||
126 | int ul_del(char *name) { | 137 | int ul_del(char *name) { |
127 | /* Test if user is already known */ | 138 | /* Test if user is already known */ |
128 | int exact_match, base = ul_nick_lookup( name, &exact_match ); | 139 | int exact_match, base = ul_nick_lookup(name, &exact_match); |
129 | if( !exact_match ) return -1; | 140 | if (!exact_match) |
141 | return -1; | ||
130 | 142 | ||
131 | /* Release the name buffer */ | 143 | /* Release the name buffer */ |
132 | free( g_users[base].nick ); | 144 | free(g_users[base].nick); |
133 | if( g_users[base].flags & UL_ME ) g_nick = 0; | 145 | if (g_users[base].flags & UL_ME) |
146 | g_nick = 0; | ||
134 | 147 | ||
135 | /* Copy the tail */ | 148 | /* Copy the tail */ |
136 | memmove( g_users + base, g_users + base + 1, ( g_users_count - base - 1 ) * sizeof( user ) ); | 149 | memmove(g_users + base, g_users + base + 1, |
150 | (g_users_count - base - 1) * sizeof(user)); | ||
137 | 151 | ||
138 | /* Shrink user list, realloc to a smaller size never fails */ | 152 | /* Shrink user list, realloc to a smaller size never fails */ |
139 | g_users = realloc( g_users, sizeof( user ) * --g_users_count ); | 153 | g_users = realloc(g_users, sizeof(user) * --g_users_count); |
140 | return 0; | 154 | return 0; |
141 | } | 155 | } |
142 | 156 | ||
143 | int ul_rename(char *oldname, char *newname) { | 157 | int ul_rename(char *oldname, char *newname) { |
144 | /* Ensure user */ | 158 | /* Ensure user */ |
145 | int base = ul_add( oldname, -1 ); | 159 | int base = ul_add(oldname, -1); |
146 | if( base >= 0 ) { | 160 | if (base >= 0) { |
147 | free( g_users[base].nick ); | 161 | free(g_users[base].nick); |
148 | g_users[base].nick = strdup( newname ); | 162 | g_users[base].nick = strdup(newname); |
149 | if( g_users[base].flags & UL_ME ) | 163 | if (g_users[base].flags & UL_ME) |
150 | g_nick = g_users[base].nick; | 164 | g_nick = g_users[base].nick; |
151 | if( g_users[base].flags & UL_IN_MY_CHAN ) | 165 | if (g_users[base].flags & UL_IN_MY_CHAN) |
152 | ul_public_action(newname); | 166 | ul_public_action(newname); |
153 | } | 167 | } |
154 | return base; | 168 | return base; |
@@ -156,23 +170,23 @@ int ul_rename(char *oldname, char *newname) { | |||
156 | 170 | ||
157 | void ul_clear() { | 171 | void ul_clear() { |
158 | int i; | 172 | int i; |
159 | for( i=0; i<g_users_count; ++i ) | 173 | for (i = 0; i < g_users_count; ++i) |
160 | free( g_users[i].nick ); | 174 | free(g_users[i].nick); |
161 | free( g_users ); | 175 | free(g_users); |
162 | g_nick = 0; | 176 | g_nick = 0; |
163 | } | 177 | } |
164 | 178 | ||
165 | void ul_rebuild_list( ) { | 179 | void ul_rebuild_list() { |
166 | int i; | 180 | int i; |
167 | for( i=0; i<g_users_count; ++i ) | 181 | for (i = 0; i < g_users_count; ++i) |
168 | g_users[i].flags |= UL_NOT_IN_LIST; | 182 | g_users[i].flags |= UL_NOT_IN_LIST; |
169 | } | 183 | } |
170 | 184 | ||
171 | void ul_clean() { | 185 | void ul_clean() { |
172 | int i; | 186 | int i; |
173 | for( i=0; i<g_users_count; ++i ) { | 187 | for (i = 0; i < g_users_count; ++i) { |
174 | if( g_users[i].flags & UL_NOT_IN_LIST ) { | 188 | if (g_users[i].flags & UL_NOT_IN_LIST) { |
175 | ul_del( g_users[i].nick ); | 189 | ul_del(g_users[i].nick); |
176 | --i; | 190 | --i; |
177 | } | 191 | } |
178 | } | 192 | } |
@@ -187,237 +201,270 @@ void ul_leave_chan(char *name) { | |||
187 | void ul_enter_chan(char *name) { | 201 | void ul_enter_chan(char *name) { |
188 | /* Ensure user and put him on the channel */ | 202 | /* Ensure user and put him on the channel */ |
189 | int base = ul_add(name, 1); | 203 | int base = ul_add(name, 1); |
190 | if( base >= 0 ) | 204 | if (base >= 0) |
191 | ul_public_action(name); | 205 | ul_public_action(name); |
192 | 206 | ||
193 | /* Reflect in UI */ | 207 | /* Reflect in UI */ |
194 | if( own_nick_check( name ) ) | 208 | if (own_nick_check(name)) |
195 | ownjoin( g_channel ); | 209 | ownjoin(g_channel); |
196 | } | 210 | } |
197 | 211 | ||
198 | void ul_private_action(char *name) { | 212 | void ul_private_action(char *name) { |
199 | /* Ensure user and keep channel state */ | 213 | /* Ensure user and keep channel state */ |
200 | int base = ul_add(name, -1); | 214 | int base = ul_add(name, -1); |
201 | if( base >= 0 ) | 215 | if (base >= 0) |
202 | g_users[base].last_private = ul_now(); | 216 | g_users[base].last_private = ul_now(); |
203 | } | 217 | } |
204 | 218 | ||
205 | void ul_public_action(char *name) { | 219 | void ul_public_action(char *name) { |
206 | /* Ensure user and put him on the channel */ | 220 | /* Ensure user and put him on the channel */ |
207 | int base = ul_add(name, 1); | 221 | int base = ul_add(name, 1); |
208 | if( base >= 0 ) | 222 | if (base >= 0) |
209 | g_users[base].last_public = ul_now(); | 223 | g_users[base].last_public = ul_now(); |
210 | } | 224 | } |
211 | 225 | ||
212 | void ul_add_to_dict(char *dict_items) { | 226 | void ul_add_to_dict(char *dict_items) { |
213 | char *i; | 227 | char *i; |
214 | for(i=strtok(dict_items," ");i;i=strtok(0," ")) { | 228 | for (i = strtok(dict_items, " "); i; i = strtok(0, " ")) { |
215 | g_dict = realloc( g_dict, sizeof(char*) * ( 1 + g_dict_len ) ); | 229 | g_dict = realloc(g_dict, sizeof(char *) * (1 + g_dict_len)); |
216 | if( !g_dict ) exit(1); | 230 | if (!g_dict) |
231 | exit(1); | ||
217 | g_dict[g_dict_len++] = strdup(i); | 232 | g_dict[g_dict_len++] = strdup(i); |
218 | } | 233 | } |
219 | } | 234 | } |
220 | 235 | ||
221 | /* Finding users ul_finduser? */ | 236 | /* Finding users ul_finduser? */ |
222 | char * ul_match_user(char *regex) { | 237 | char *ul_match_user(char *regex) { |
223 | char *dest = tmpstr; | 238 | char *dest = tmpstr; |
224 | int i; | 239 | int i; |
225 | regex_t preg; | 240 | regex_t preg; |
226 | 241 | ||
227 | *dest = 0; | 242 | *dest = 0; |
228 | if( !regcomp( &preg, regex, REG_ICASE | REG_EXTENDED | REG_NEWLINE)) { | 243 | if (!regcomp(&preg, regex, REG_ICASE | REG_EXTENDED | REG_NEWLINE)) { |
229 | 244 | ||
230 | /* does the username match? */ | 245 | /* does the username match? */ |
231 | /* XXX overflow for too many matches */ | 246 | /* XXX overflow for too many matches */ |
232 | for( i=0; i<g_users_count; ++i ) | 247 | for (i = 0; i < g_users_count; ++i) |
233 | if( !regexec( &preg, g_users[i].nick, 0, NULL, 0)) /* append username to list */ | 248 | if (!regexec(&preg, g_users[i].nick, 0, NULL, |
234 | dest += snprintf ( dest, 256, " %s", g_users[i].nick); | 249 | 0)) /* append username to list */ |
235 | 250 | dest += snprintf(dest, 256, " %s", g_users[i].nick); | |
236 | } | 251 | } |
237 | regfree( &preg ); | 252 | regfree(&preg); |
238 | return tmpstr; | 253 | return tmpstr; |
239 | } | 254 | } |
240 | 255 | ||
241 | static int ul_compare_private( const void *a, const void *b ) { | 256 | static int ul_compare_private(const void *a, const void *b) { |
242 | const user *_a = (const user *)a, *_b = (const user *)b; | 257 | const user *_a = (const user *)a, *_b = (const user *)b; |
243 | if( _a->last_private > _b->last_private ) return -1; | 258 | if (_a->last_private > _b->last_private) |
259 | return -1; | ||
244 | return 1; | 260 | return 1; |
245 | } | 261 | } |
246 | 262 | ||
247 | static int ul_compare_begin_of_line_ncase( const void *a, const void *b ) { | 263 | static int ul_compare_begin_of_line_ncase(const void *a, const void *b) { |
248 | const user *_a = (const user *)a, *_b = (const user *)b; | 264 | const user *_a = (const user *)a, *_b = (const user *)b; |
249 | size_t tmpstr_len; | 265 | size_t tmpstr_len; |
250 | int a_i, b_i; | 266 | int a_i, b_i; |
251 | 267 | ||
252 | /* First ensure that users in current channel win */ | 268 | /* First ensure that users in current channel win */ |
253 | if( !(_a->flags & UL_IN_MY_CHAN ) ) return 1; | 269 | if (!(_a->flags & UL_IN_MY_CHAN)) |
254 | if( !(_b->flags & UL_IN_MY_CHAN ) ) return -1; | 270 | return 1; |
255 | 271 | if (!(_b->flags & UL_IN_MY_CHAN)) | |
256 | tmpstr_len = strlen( tmpstr ); | 272 | return -1; |
257 | a_i = strncasecmp( _a->nick, tmpstr, tmpstr_len ); | 273 | |
258 | b_i = strncasecmp( _b->nick, tmpstr, tmpstr_len ); | 274 | tmpstr_len = strlen(tmpstr); |
259 | 275 | a_i = strncasecmp(_a->nick, tmpstr, tmpstr_len); | |
260 | if( a_i && b_i ) return 0; // Both nicks dont match | 276 | b_i = strncasecmp(_b->nick, tmpstr, tmpstr_len); |
261 | if( !a_i && b_i ) return -1; // a matches insensitive, b doesnt | 277 | |
262 | if( a_i && !b_i ) return 1; // b matches insensitive, a doesnt | 278 | if (a_i && b_i) |
279 | return 0; // Both nicks dont match | ||
280 | if (!a_i && b_i) | ||
281 | return -1; // a matches insensitive, b doesnt | ||
282 | if (a_i && !b_i) | ||
283 | return 1; // b matches insensitive, a doesnt | ||
263 | 284 | ||
264 | /* From here both nicks match the prefix, ensure that own_nick | 285 | /* From here both nicks match the prefix, ensure that own_nick |
265 | always appears last */ | 286 | always appears last */ |
266 | if( _a->flags & UL_ME ) return 1; | 287 | if (_a->flags & UL_ME) |
267 | if( _b->flags & UL_ME ) return -1; | 288 | return 1; |
289 | if (_b->flags & UL_ME) | ||
290 | return -1; | ||
268 | 291 | ||
269 | /* Now the user with the most recent public activity wins */ | 292 | /* Now the user with the most recent public activity wins */ |
270 | if( _a->last_public > _b->last_public ) return -1; | 293 | if (_a->last_public > _b->last_public) |
294 | return -1; | ||
271 | 295 | ||
272 | return 1; | 296 | return 1; |
273 | } | 297 | } |
274 | 298 | ||
275 | static int ul_compare_begin_of_line_case( const void *a, const void *b ) { | 299 | static int ul_compare_begin_of_line_case(const void *a, const void *b) { |
276 | const user *_a = (const user *)a, *_b = (const user *)b; | 300 | const user *_a = (const user *)a, *_b = (const user *)b; |
277 | size_t tmpstr_len; | 301 | size_t tmpstr_len; |
278 | int a_i, b_i, a_s, b_s; | 302 | int a_i, b_i, a_s, b_s; |
279 | 303 | ||
280 | /* First ensure that users in current channel win */ | 304 | /* First ensure that users in current channel win */ |
281 | if( !(_a->flags & UL_IN_MY_CHAN ) ) return 1; | 305 | if (!(_a->flags & UL_IN_MY_CHAN)) |
282 | if( !(_b->flags & UL_IN_MY_CHAN ) ) return -1; | 306 | return 1; |
283 | 307 | if (!(_b->flags & UL_IN_MY_CHAN)) | |
284 | tmpstr_len = strlen( tmpstr ); | 308 | return -1; |
285 | a_i = strncasecmp( _a->nick, tmpstr, tmpstr_len ); | 309 | |
286 | a_s = strncmp ( _a->nick, tmpstr, tmpstr_len ); | 310 | tmpstr_len = strlen(tmpstr); |
287 | b_i = strncasecmp( _b->nick, tmpstr, tmpstr_len ); | 311 | a_i = strncasecmp(_a->nick, tmpstr, tmpstr_len); |
288 | b_s = strncmp ( _b->nick, tmpstr, tmpstr_len ); | 312 | a_s = strncmp(_a->nick, tmpstr, tmpstr_len); |
289 | 313 | b_i = strncasecmp(_b->nick, tmpstr, tmpstr_len); | |
290 | if( a_i && b_i ) return 0; // Both nicks dont match at all | 314 | b_s = strncmp(_b->nick, tmpstr, tmpstr_len); |
291 | if( !a_i && b_i ) return -1; // a matches insensitive, b doesnt | 315 | |
292 | if( a_i && !b_i ) return 1; // b matches insensitive, a doesnt | 316 | if (a_i && b_i) |
293 | 317 | return 0; // Both nicks dont match at all | |
294 | if( !a_s && b_s ) return -1; // a matches sensitive, b doesnt | 318 | if (!a_i && b_i) |
295 | if( a_s && !b_s ) return 1; // b matches sensitive, a doesnt | 319 | return -1; // a matches insensitive, b doesnt |
320 | if (a_i && !b_i) | ||
321 | return 1; // b matches insensitive, a doesnt | ||
322 | |||
323 | if (!a_s && b_s) | ||
324 | return -1; // a matches sensitive, b doesnt | ||
325 | if (a_s && !b_s) | ||
326 | return 1; // b matches sensitive, a doesnt | ||
296 | 327 | ||
297 | /* From now we know that both match with same quality, ensure | 328 | /* From now we know that both match with same quality, ensure |
298 | that own nick always appears last */ | 329 | that own nick always appears last */ |
299 | if( _a->flags & UL_ME ) return 1; | 330 | if (_a->flags & UL_ME) |
300 | if( _b->flags & UL_ME ) return -1; | 331 | return 1; |
332 | if (_b->flags & UL_ME) | ||
333 | return -1; | ||
301 | 334 | ||
302 | /* Now the user with the most recent public activity wins */ | 335 | /* Now the user with the most recent public activity wins */ |
303 | if( _a->last_public > _b->last_public ) return -1; | 336 | if (_a->last_public > _b->last_public) |
337 | return -1; | ||
304 | 338 | ||
305 | return 1; | 339 | return 1; |
306 | } | 340 | } |
307 | 341 | ||
308 | static int ul_compare_middle_ncase( const void *a, const void *b ) { | 342 | static int ul_compare_middle_ncase(const void *a, const void *b) { |
309 | const user *_a = (const user *)a, *_b = (const user *)b; | 343 | const user *_a = (const user *)a, *_b = (const user *)b; |
310 | 344 | ||
311 | /* Ensure that own nick appears last in list */ | 345 | /* Ensure that own nick appears last in list */ |
312 | if( _a->flags & UL_ME ) return 1; | 346 | if (_a->flags & UL_ME) |
313 | if( _b->flags & UL_ME ) return -1; | 347 | return 1; |
348 | if (_b->flags & UL_ME) | ||
349 | return -1; | ||
314 | 350 | ||
315 | return strcasecmp( _a->nick, _b->nick ); | 351 | return strcasecmp(_a->nick, _b->nick); |
316 | } | 352 | } |
317 | 353 | ||
318 | static int ul_compare_middle_case( const void *a, const void *b ) { | 354 | static int ul_compare_middle_case(const void *a, const void *b) { |
319 | const user *_a = (const user *)a, *_b = (const user *)b; | 355 | const user *_a = (const user *)a, *_b = (const user *)b; |
320 | size_t tmpstr_len; | 356 | size_t tmpstr_len; |
321 | int a_s, b_s; | 357 | int a_s, b_s; |
322 | 358 | ||
323 | /* Ensure that own nick appears last in list */ | 359 | /* Ensure that own nick appears last in list */ |
324 | if( _a->flags & UL_ME ) return 1; | 360 | if (_a->flags & UL_ME) |
325 | if( _b->flags & UL_ME ) return -1; | 361 | return 1; |
362 | if (_b->flags & UL_ME) | ||
363 | return -1; | ||
326 | 364 | ||
327 | tmpstr_len = strlen( tmpstr ); | 365 | tmpstr_len = strlen(tmpstr); |
328 | a_s = strncmp( _a->nick, tmpstr, tmpstr_len ); | 366 | a_s = strncmp(_a->nick, tmpstr, tmpstr_len); |
329 | b_s = strncmp( _b->nick, tmpstr, tmpstr_len ); | 367 | b_s = strncmp(_b->nick, tmpstr, tmpstr_len); |
330 | 368 | ||
331 | if( !a_s && b_s ) return -1; // a matches sensitive, b doesnt | 369 | if (!a_s && b_s) |
332 | if( a_s && !b_s ) return 1; // b matches sensitive, a doesnt | 370 | return -1; // a matches sensitive, b doesnt |
371 | if (a_s && !b_s) | ||
372 | return 1; // b matches sensitive, a doesnt | ||
333 | 373 | ||
334 | /* From now both strings either both or both dont match | 374 | /* From now both strings either both or both dont match |
335 | decide their position by case insensitive match */ | 375 | decide their position by case insensitive match */ |
336 | return strcasecmp( _a->nick, _b->nick ); | 376 | return strcasecmp(_a->nick, _b->nick); |
337 | } | 377 | } |
338 | 378 | ||
339 | /* Nick completion function for readline */ | 379 | /* Nick completion function for readline */ |
340 | char **ul_complete_user(char *text, int start, int end ) { | 380 | char **ul_complete_user(char *text, int start, int end) { |
341 | char **result = 0; | 381 | char **result = 0; |
342 | int i, result_count = 0, dict_result_count = 0; | 382 | int i, result_count = 0, dict_result_count = 0; |
343 | 383 | ||
344 | /* Never want readline to complete filenames */ | 384 | /* Never want readline to complete filenames */ |
345 | rl_attempted_completion_over = 1; | 385 | rl_attempted_completion_over = 1; |
346 | 386 | ||
347 | /* Check for amount of custom dict matches */ | 387 | /* Check for amount of custom dict matches */ |
348 | if( end && ( start != end ) ) | 388 | if (end && (start != end)) |
349 | for( i=0; i<g_dict_len; ++i ) | 389 | for (i = 0; i < g_dict_len; ++i) |
350 | if( !strncasecmp( g_dict[i], text+start, end-start ) ) | 390 | if (!strncasecmp(g_dict[i], text + start, end - start)) |
351 | ++dict_result_count; | 391 | ++dict_result_count; |
352 | 392 | ||
353 | /* Prepare return array ... of max g_users_count (char*) | 393 | /* Prepare return array ... of max g_users_count (char*) |
354 | Plus least common prefix in [0] and null terminator | 394 | Plus least common prefix in [0] and null terminator |
355 | */ | 395 | */ |
356 | result = malloc( sizeof(char*) * ( 2 + g_users_count + dict_result_count ) ); | 396 | result = malloc(sizeof(char *) * (2 + g_users_count + dict_result_count)); |
357 | if( !result ) return 0; | 397 | if (!result) |
398 | return 0; | ||
358 | 399 | ||
359 | if( start == 0 && end == 0 ) { | 400 | if (start == 0 && end == 0) { |
360 | /* Completion on begin of line yields list of everyone we | 401 | /* Completion on begin of line yields list of everyone we |
361 | were in private conversation, sorted by time of last .m */ | 402 | were in private conversation, sorted by time of last .m */ |
362 | qsort( g_users, g_users_count, sizeof(user), ul_compare_private ); | 403 | qsort(g_users, g_users_count, sizeof(user), ul_compare_private); |
363 | for( i=0; i<g_users_count; ++i ) | 404 | for (i = 0; i < g_users_count; ++i) |
364 | if( g_users[i].last_private ) { | 405 | if (g_users[i].last_private) { |
365 | snprintf( tmpstr, TMPSTRSIZE, ".m %s", g_users[i].nick ); | 406 | snprintf(tmpstr, TMPSTRSIZE, ".m %s", g_users[i].nick); |
366 | result[++result_count] = strdup(tmpstr); | 407 | result[++result_count] = strdup(tmpstr); |
367 | } | 408 | } |
368 | /* No common prefix */ | 409 | /* No common prefix */ |
369 | if( result_count ) result[0] = strdup(""); | 410 | if (result_count) |
411 | result[0] = strdup(""); | ||
370 | 412 | ||
371 | } else if( start == 0 && end > 0 ) { | 413 | } else if (start == 0 && end > 0) { |
372 | /* Completion on begin of line with some chars already typed yields | 414 | /* Completion on begin of line with some chars already typed yields |
373 | a list of everyone in channel, matching prefix, sorted by last | 415 | a list of everyone in channel, matching prefix, sorted by last |
374 | public activity */ | 416 | public activity */ |
375 | snprintf( tmpstr, end + 1, "%s", text ); | 417 | snprintf(tmpstr, end + 1, "%s", text); |
376 | if( ul_case_first ) | 418 | if (ul_case_first) |
377 | qsort( g_users, g_users_count, sizeof(user), ul_compare_begin_of_line_case ); | 419 | qsort(g_users, g_users_count, sizeof(user), |
420 | ul_compare_begin_of_line_case); | ||
378 | else | 421 | else |
379 | qsort( g_users, g_users_count, sizeof(user), ul_compare_begin_of_line_ncase ); | 422 | qsort(g_users, g_users_count, sizeof(user), |
423 | ul_compare_begin_of_line_ncase); | ||
380 | 424 | ||
381 | for( i=0; i<g_users_count; ++i ) | 425 | for (i = 0; i < g_users_count; ++i) |
382 | if( ( g_users[i].flags & UL_IN_MY_CHAN ) && !strncasecmp( g_users[i].nick, tmpstr, end ) ) { | 426 | if ((g_users[i].flags & UL_IN_MY_CHAN) && |
383 | snprintf( tmpstr, TMPSTRSIZE, "%s:", g_users[i].nick ); | 427 | !strncasecmp(g_users[i].nick, tmpstr, end)) { |
428 | snprintf(tmpstr, TMPSTRSIZE, "%s:", g_users[i].nick); | ||
384 | result[++result_count] = strdup(tmpstr); | 429 | result[++result_count] = strdup(tmpstr); |
385 | } | 430 | } |
386 | 431 | ||
387 | /* Copy matches from personal dict to the end */ | 432 | /* Copy matches from personal dict to the end */ |
388 | for( i=0; i<g_dict_len; ++i ) | 433 | for (i = 0; i < g_dict_len; ++i) |
389 | if( !strncasecmp( g_dict[i], tmpstr, end-start ) ) { | 434 | if (!strncasecmp(g_dict[i], tmpstr, end - start)) { |
390 | snprintf( tmpstr, TMPSTRSIZE, "%s:", g_dict[i] ); | 435 | snprintf(tmpstr, TMPSTRSIZE, "%s:", g_dict[i]); |
391 | result[++result_count] = strdup(tmpstr); | 436 | result[++result_count] = strdup(tmpstr); |
392 | } | 437 | } |
393 | 438 | ||
394 | /* Copy common prefix */ | 439 | /* Copy common prefix */ |
395 | if( result_count ) result[0] = strndup(text, end); | 440 | if (result_count) |
396 | } else if( start != end ) { | 441 | result[0] = strndup(text, end); |
442 | } else if (start != end) { | ||
397 | /* Completion in the middle of the line most likely is a .m XY<TAB> | 443 | /* Completion in the middle of the line most likely is a .m XY<TAB> |
398 | and thus should complete all users, sorted alphabetically without | 444 | and thus should complete all users, sorted alphabetically without |
399 | preferences. */ | 445 | preferences. */ |
400 | snprintf( tmpstr, end - start + 1, "%s", text ); | 446 | snprintf(tmpstr, end - start + 1, "%s", text); |
401 | if( ul_case_first ) | 447 | if (ul_case_first) |
402 | qsort( g_users, g_users_count, sizeof(user), ul_compare_middle_case ); | 448 | qsort(g_users, g_users_count, sizeof(user), ul_compare_middle_case); |
403 | else | 449 | else |
404 | qsort( g_users, g_users_count, sizeof(user), ul_compare_middle_ncase ); | 450 | qsort(g_users, g_users_count, sizeof(user), ul_compare_middle_ncase); |
405 | 451 | ||
406 | for( i=0; i<g_users_count; ++i ) | 452 | for (i = 0; i < g_users_count; ++i) |
407 | if( !strncasecmp( g_users[i].nick, tmpstr, end - start ) ) | 453 | if (!strncasecmp(g_users[i].nick, tmpstr, end - start)) |
408 | result[++result_count] = strdup(g_users[i].nick); | 454 | result[++result_count] = strdup(g_users[i].nick); |
409 | 455 | ||
410 | /* Copy matches from personal dict to the end */ | 456 | /* Copy matches from personal dict to the end */ |
411 | for( i=0; i<g_dict_len; ++i ) | 457 | for (i = 0; i < g_dict_len; ++i) |
412 | if( !strncasecmp( g_dict[i], tmpstr, end-start ) ) | 458 | if (!strncasecmp(g_dict[i], tmpstr, end - start)) |
413 | result[++result_count] = strdup(g_dict[i]); | 459 | result[++result_count] = strdup(g_dict[i]); |
414 | 460 | ||
415 | /* Copy common prefix */ | 461 | /* Copy common prefix */ |
416 | if( result_count ) result[0] = strndup(text, end - start); | 462 | if (result_count) |
463 | result[0] = strndup(text, end - start); | ||
417 | } /* else: completion of an empty word in the middle yields nothing */ | 464 | } /* else: completion of an empty word in the middle yields nothing */ |
418 | 465 | ||
419 | if( !result_count ) { | 466 | if (!result_count) { |
420 | free( result ); | 467 | free(result); |
421 | result = 0; | 468 | result = 0; |
422 | } else | 469 | } else |
423 | result[++result_count] = 0; | 470 | result[++result_count] = 0; |
@@ -10,7 +10,7 @@ | |||
10 | * without even the implied warranty of merchantability or fitness for a | 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 | 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 | 12 | * any direct, indirect, incidental or special damages arising in any way out |
13 | * of the use of this software. | 13 | * of the use of this software. |
14 | * | 14 | * |
15 | */ | 15 | */ |
16 | 16 | ||
@@ -18,56 +18,127 @@ | |||
18 | typedef enum { SM_IGNORE, SM_INFO, SM_USERINFO, SM_CHANNEL, SM_ERROR } smtype; | 18 | typedef enum { SM_IGNORE, SM_INFO, SM_USERINFO, SM_CHANNEL, SM_ERROR } smtype; |
19 | 19 | ||
20 | /* servermessage structure */ | 20 | /* servermessage structure */ |
21 | struct servermessage | 21 | struct servermessage { |
22 | { | 22 | char id[4]; /* three-character message id */ |
23 | char id[4]; /* three-character message id */ | 23 | smtype type; /* message type */ |
24 | smtype type; /* message type */ | 24 | void (*funct)(char *); /* function used by client */ |
25 | void (*funct) (char *); /* function used by client */ | 25 | void (*hook)(char *); /* function hook for scripting */ |
26 | void (*hook) (char *); /* function hook for scripting */ | ||
27 | }; | 26 | }; |
28 | typedef struct servermessage servermessage; | 27 | typedef struct servermessage servermessage; |
29 | 28 | ||
30 | /* configuration types and variable numbers */ | 29 | /* configuration types and variable numbers */ |
31 | typedef enum { CO_NIL, CO_STR, CO_INT } conftype; | 30 | typedef enum { CO_NIL, CO_STR, CO_INT } conftype; |
32 | typedef enum { CF_NIL, CF_NICK, CF_FROM, CF_SERVERHOST, CF_SERVERPORT, | 31 | typedef enum { |
33 | CF_CIPHERSUITE, CF_CONFIGFILE, CF_CERTFILE, CF_KEYFILE, CF_FORMFILE, | 32 | CF_NIL, |
34 | CF_LOGINSCRIPT, CF_FINGERPRINT, CF_PINFINGER, CF_USESSL, CF_IGNSSL, CF_VERIFYSSL, CF_USECERT, | 33 | CF_NICK, |
35 | CF_PRIVHEIGHT, CF_PRIVCOLLAPS, CF_HSCROLL, CF_CHANNEL, CF_USETIME, CF_USETOPIC, | 34 | CF_FROM, |
36 | CF_SCROLLBPRIV, CF_SCROLLBACK, CF_SCROLLBPRIVT, CF_SCROLLBACKT, CF_ENCODING, | 35 | CF_SERVERHOST, |
37 | CF_BELLPRIV, CF_CASEFIRST, CF_AUTORECONN, CF_KEEPALIVE } confopt; | 36 | CF_SERVERPORT, |
37 | CF_CIPHERSUITE, | ||
38 | CF_CONFIGFILE, | ||
39 | CF_CERTFILE, | ||
40 | CF_KEYFILE, | ||
41 | CF_CAFILE, | ||
42 | CF_FORMFILE, | ||
43 | CF_LOGINSCRIPT, | ||
44 | CF_FINGERPRINT, | ||
45 | CF_PINFINGER, | ||
46 | CF_USESSL, | ||
47 | CF_TLSLIB, | ||
48 | CF_IGNSSL, | ||
49 | CF_VERIFYSSL, | ||
50 | CF_USECERT, | ||
51 | CF_PRIVHEIGHT, | ||
52 | CF_PRIVCOLLAPS, | ||
53 | CF_INVWINBAR, | ||
54 | CF_HSCROLL, | ||
55 | CF_CHANNEL, | ||
56 | CF_USETIME, | ||
57 | CF_USETOPIC, | ||
58 | CF_SCROLLBPRIV, | ||
59 | CF_SCROLLBACK, | ||
60 | CF_SCROLLBPRIVT, | ||
61 | CF_SCROLLBACKT, | ||
62 | CF_ENCODING, | ||
63 | CF_BELLPRIV, | ||
64 | CF_CASEFIRST, | ||
65 | CF_AUTORECONN, | ||
66 | CF_KEEPALIVE | ||
67 | } confopt; | ||
38 | 68 | ||
39 | /* format strings */ | 69 | /* format strings */ |
40 | typedef enum { FS_PLAIN, FS_CHAN, FS_PRIV, FS_SERV, FS_GLOB, FS_DBG, FS_ERR, | 70 | typedef enum { |
41 | FS_IDLE, FS_TIME, FS_CONSOLETIME, FS_TOPICW, FS_NOTOPICW, FS_CONSOLE, FS_CONNECTED, FS_CANTCONNECT, | 71 | FS_PLAIN, |
42 | FS_TOPIC, FS_NOTOPIC, FS_CHGTOPIC, FS_USONLINE, FS_USMATCH, FS_SIGNON, FS_SIGNOFF, | 72 | FS_CHAN, |
43 | FS_JOIN, FS_LEAVE, FS_NICKCHANGE, FS_UNKNOWNMSG, FS_BOGUSMSG, FS_RXPUBURL, | 73 | FS_PRIV, |
44 | FS_MYPUBURL, FS_RXPUBMSG, FS_MYPUBMSG, FS_TXPUBMSG, FS_RXPRIVMSG, FS_TXPRIVMSG, | 74 | FS_SERV, |
45 | FS_BGPRIVMSG, FS_PUBTHOUGHT, FS_TXPUBTHOUGHT, FS_TXPUBNTHOUGHT, FS_PUBACTION, | 75 | FS_GLOB, |
46 | FS_TXPUBACTION, FS_BGTXPUBACTION, FS_COMMAND, FS_LOCALCOMMAND, FS_BOGUSCOMMAND, | 76 | FS_DBG, |
47 | FS_SBINF, FS_MISSTYPED, FS_UNKNCMD, FS_BADREGEX, FS_ERR_STRING } formtstr; | 77 | FS_ERR, |
78 | FS_IDLE, | ||
79 | FS_TIME, | ||
80 | FS_CONSOLETIME, | ||
81 | FS_TOPICW, | ||
82 | FS_NOTOPICW, | ||
83 | FS_CONSOLE, | ||
84 | FS_CONNECTED, | ||
85 | FS_CANTCONNECT, | ||
86 | FS_TOPIC, | ||
87 | FS_NOTOPIC, | ||
88 | FS_CHGTOPIC, | ||
89 | FS_USONLINE, | ||
90 | FS_USMATCH, | ||
91 | FS_SIGNON, | ||
92 | FS_SIGNOFF, | ||
93 | FS_JOIN, | ||
94 | FS_LEAVE, | ||
95 | FS_NICKCHANGE, | ||
96 | FS_UNKNOWNMSG, | ||
97 | FS_BOGUSMSG, | ||
98 | FS_RXPUBURL, | ||
99 | FS_MYPUBURL, | ||
100 | FS_RXPUBMSG, | ||
101 | FS_MYPUBMSG, | ||
102 | FS_TXPUBMSG, | ||
103 | FS_RXPRIVMSG, | ||
104 | FS_TXPRIVMSG, | ||
105 | FS_BGPRIVMSG, | ||
106 | FS_PUBTHOUGHT, | ||
107 | FS_TXPUBTHOUGHT, | ||
108 | FS_TXPUBNTHOUGHT, | ||
109 | FS_PUBACTION, | ||
110 | FS_TXPUBACTION, | ||
111 | FS_BGTXPUBACTION, | ||
112 | FS_COMMAND, | ||
113 | FS_LOCALCOMMAND, | ||
114 | FS_BOGUSCOMMAND, | ||
115 | FS_SBINF, | ||
116 | FS_MISSTYPED, | ||
117 | FS_UNKNCMD, | ||
118 | FS_BADREGEX, | ||
119 | FS_ERR_STRING | ||
120 | } formtstr; | ||
48 | 121 | ||
49 | /* configoption structure */ | 122 | /* configoption structure */ |
50 | struct configoption | 123 | struct configoption { |
51 | { | 124 | confopt id; |
52 | confopt id; | ||
53 | conftype type; | 125 | conftype type; |
54 | char *varname; | 126 | char *varname; |
55 | char *defaultvalue; | 127 | char *defaultvalue; |
56 | char *value; | 128 | char *value; |
57 | union { | 129 | union { |
58 | char **pstr; | 130 | char **pstr; |
59 | unsigned int *pint; | 131 | unsigned int *pint; |
60 | } localvar; | 132 | } localvar; |
61 | }; | 133 | }; |
62 | 134 | ||
63 | typedef struct configoption configoption; | 135 | typedef struct configoption configoption; |
64 | 136 | ||
65 | /* format strings */ | 137 | /* format strings */ |
66 | struct formatstring | 138 | struct formatstring { |
67 | { | ||
68 | formtstr id; | 139 | formtstr id; |
69 | char *idstring; | 140 | char *idstring; |
70 | char *formatstr; | 141 | char *formatstr; |
71 | }; | 142 | }; |
72 | typedef struct formatstring formatstring; | 143 | typedef struct formatstring formatstring; |
73 | 144 | ||
@@ -82,16 +153,16 @@ extern unsigned int want_tcp_keepalive; | |||
82 | #define ERRSTRSIZE 1024 | 153 | #define ERRSTRSIZE 1024 |
83 | extern char errstr[]; | 154 | extern char errstr[]; |
84 | extern const char *vchat_cl_version; | 155 | extern const char *vchat_cl_version; |
85 | void loadcfg (char *file,int complain,void (*lineparser) (char *)); | 156 | void loadcfg(char *file, int complain, void (*lineparser)(char *)); |
86 | void loadformats (char *file); | 157 | void loadformats(char *file); |
87 | void cleanup(int signal); | 158 | void cleanup(int signal); |
88 | 159 | ||
89 | /* configuration helper funktions from vchat-client.c */ | 160 | /* configuration helper functions from vchat-client.c */ |
90 | char *getformatstr (formtstr id); | 161 | char *getformatstr(formtstr id); |
91 | char *getstroption (confopt option); | 162 | char *getstroption(confopt option); |
92 | void setstroption (confopt option, char *string); | 163 | void setstroption(confopt option, char *string); |
93 | int getintoption (confopt option); | 164 | int getintoption(confopt option); |
94 | void setintoption (confopt option, int value); | 165 | void setintoption(confopt option, int value); |
95 | 166 | ||
96 | /* vchat-ui.c */ | 167 | /* vchat-ui.c */ |
97 | extern const char *vchat_ui_version; | 168 | extern const char *vchat_ui_version; |
@@ -104,76 +175,75 @@ extern char consolestr[]; | |||
104 | extern char *encoding; | 175 | extern char *encoding; |
105 | 176 | ||
106 | /* init / exit functions */ | 177 | /* init / exit functions */ |
107 | void initui (void); | 178 | void initui(void); |
108 | void exitui (void); | 179 | void exitui(void); |
109 | 180 | ||
110 | /* called from eventloop in vchat-client.c */ | 181 | /* called from eventloop in vchat-client.c */ |
111 | void userinput (void); | 182 | void userinput(void); |
112 | 183 | ||
113 | /* display various messages */ | 184 | /* display various messages */ |
114 | int writechan (char *str); | 185 | int writechan(char *str); |
115 | int writepriv (char *str, int maybeep ); | 186 | int writepriv(char *str, int maybeep); |
116 | void writeout (const char *str); | 187 | void writeout(const char *str); |
117 | void showout (void); | 188 | void showout(void); |
118 | void flushout (void); | 189 | void flushout(void); |
119 | #define msgout(STR) {flushout();writeout(STR);showout();} | 190 | #define msgout(STR) \ |
120 | void hideout (void); | 191 | { \ |
121 | int writecf (formtstr id, char *str); | 192 | flushout(); \ |
193 | writeout(STR); \ | ||
194 | showout(); \ | ||
195 | } | ||
196 | void hideout(void); | ||
197 | int writecf(formtstr id, char *str); | ||
198 | /* dumps aggregated connect output in case of a connection error */ | ||
199 | void dumpconnect(); | ||
200 | void flushconnect(); | ||
122 | 201 | ||
123 | extern int outputcountdown; | 202 | extern int outputcountdown; |
124 | 203 | ||
125 | /* update console / topic window */ | 204 | /* update console / topic window */ |
126 | void consoleline (char *); | 205 | void consoleline(char *); |
127 | void topicline (char *); | 206 | void topicline(char *); |
128 | 207 | ||
129 | /* prompt for nick or password */ | 208 | /* prompt for nick or password */ |
130 | void nickprompt (void); | 209 | void nickprompt(void); |
131 | int passprompt (char *buf, int size, int rwflag, void *userdata); | 210 | int passprompt(char *buf, int size, int rwflag, void *userdata); |
132 | 211 | ||
133 | /* filter functions */ | 212 | /* filter functions */ |
134 | void refilterscrollback( void); | 213 | void refilterscrollback(void); |
135 | 214 | ||
136 | unsigned int addfilter ( char colour, char *regex ); | 215 | unsigned int addfilter(char colour, char *regex); |
137 | void removefilter ( char *line ); | 216 | void removefilter(char *line); |
138 | void listfilters ( void ); | 217 | void listfilters(void); |
139 | void clearfilters ( char colour ); | 218 | void clearfilters(char colour); |
140 | 219 | ||
141 | void handlequery ( char *line ); | 220 | void handlequery(char *line); |
142 | 221 | ||
143 | /* vchat-protocol.c */ | 222 | /* vchat-protocol.c */ |
144 | extern const char *vchat_io_version; | 223 | extern const char *vchat_io_version; |
145 | 224 | void protocol_parsemsg(char *message); | |
146 | /* connect/disconnect */ | ||
147 | int vcconnect (char *server, char *port); | ||
148 | void vcdisconnect (); | ||
149 | |||
150 | /* network I/O */ | ||
151 | void networkinput (void); | ||
152 | void networkoutput (char *); | ||
153 | 225 | ||
154 | /* helpers for vchat-user.c */ | 226 | /* helpers for vchat-user.c */ |
155 | void ownjoin (int channel); | 227 | void ownjoin(int channel); |
156 | void ownleave (int channel); | 228 | void ownleave(int channel); |
157 | void ownnickchange (char *newnick); | 229 | void ownnickchange(const char *newnick); |
158 | 230 | ||
159 | /* vchat-commands.c */ | 231 | /* vchat-commands.c */ |
160 | extern const char *vchat_cm_version; | 232 | extern const char *vchat_cm_version; |
161 | void command_version ( char *tail); | 233 | void command_version(char *tail); |
162 | 234 | ||
163 | /* user input */ | 235 | /* user input */ |
164 | void handleline (char *); | 236 | void handleline(char *); |
165 | 237 | ||
166 | /* struct for defining "/command" handlers */ | 238 | /* struct for defining "/command" handlers */ |
167 | typedef struct { | 239 | typedef struct { |
168 | int number; | 240 | int number; |
169 | char name[10]; | 241 | char name[10]; |
170 | int len; | 242 | int len; |
171 | void (*handler)(char *); | 243 | void (*handler)(char *); |
172 | char *short_help; | 244 | char *short_help; |
173 | char *help; | 245 | char *help; |
174 | } commandentry; | 246 | } commandentry; |
175 | 247 | ||
176 | /* vchat-ssl.c */ | 248 | /* vchat-tls.c */ |
177 | extern const char *vchat_ssl_version; | 249 | extern const char *vchat_tls_version; |
178 | extern const char *vchat_ssl_version_external; | ||
179 | void vchat_ssl_get_version_external(); | ||
diff --git a/vchatrc.ex b/vchatrc.ex index c66d1f9..c66d1f9 100755..100644 --- a/vchatrc.ex +++ b/vchatrc.ex | |||