summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDirk Engling <erdgeist@erdgeist.org>2022-05-21 14:13:28 +0200
committerDirk Engling <erdgeist@erdgeist.org>2022-05-21 14:13:28 +0200
commit24effe857346411aa6b92c7d238441ce0e6cd861 (patch)
tree20abb0897453f67c53176398601a5434d6ad7efe
parentdc6e13b39ba2190d7f11378be48f8ccfe10d47f6 (diff)
Reformatted using clang-format -format llvm
-rwxr-xr-xvchat-client.c586
-rwxr-xr-xvchat-commands.c519
-rw-r--r--vchat-connection.c133
-rw-r--r--vchat-connection.h10
-rwxr-xr-xvchat-protocol.c556
-rwxr-xr-xvchat-tls.c1036
-rwxr-xr-xvchat-tls.h16
-rwxr-xr-xvchat-ui.c1945
-rwxr-xr-xvchat-user.c399
-rwxr-xr-xvchat.h217
10 files changed, 2816 insertions, 2601 deletions
diff --git a/vchat-client.c b/vchat-client.c
index e1fb8fc..c094376 100755
--- a/vchat-client.c
+++ b/vchat-client.c
@@ -15,25 +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-connection.h"
33#include "vchat-user.h" 33#include "vchat-user.h"
34#include "vchat.h"
34 35
35/* version of this module */ 36/* version of this module */
36const char *vchat_cl_version = "vchat-client.c $Id$"; 37const char *vchat_cl_version =
38 "vchat-client.c $Id$";
37 39
38/* externally used variables */ 40/* externally used variables */
39/* we're logged in */ 41/* we're logged in */
@@ -54,139 +56,149 @@ char errstr[ERRSTRSIZE] = "\0";
54/* declaration of configuration array */ 56/* declaration of configuration array */
55#include "vchat-config.h" 57#include "vchat-config.h"
56 58
57void setnoption (const char *, char *); 59void setnoption(const char *, char *);
58 60
59static void parsecfg(char *line) { 61static void parsecfg(char *line) {
60 int bytes; 62 int bytes;
61 char *param=line; 63 char *param = line;
62 char *value=NULL; 64 char *value = NULL;
63 65
64 /* handle quotes value is empty, so we can use it */ 66 /* handle quotes value is empty, so we can use it */
65 value = strchr(line,'#'); 67 value = strchr(line, '#');
66 if (value) { /* the line contains a cute little quote */ 68 if (value) { /* the line contains a cute little quote */
67 value[0]='\0'; /* ignore the rest of the line */ 69 value[0] = '\0'; /* ignore the rest of the line */
68 } 70 }
69 71
70 /* now split the line into two parts */ 72 /* now split the line into two parts */
71 value = strchr(line,'='); 73 value = strchr(line, '=');
72 if (!value) return; /* exit if strchr fails */ 74 if (!value)
73 value[0]='\0'; 75 return; /* exit if strchr fails */
76 value[0] = '\0';
74 value++; 77 value++;
75 78
76 /* "trim" values */ 79 /* "trim" values */
77 while ((value[0] == ' ')||(value[0] == '\t')) 80 while ((value[0] == ' ') || (value[0] == '\t'))
78 value++; 81 value++;
79 bytes = strlen(value); 82 bytes = strlen(value);
80 while ((value[bytes-1] == ' ')||(value[bytes-1] == '\t')) { 83 while ((value[bytes - 1] == ' ') || (value[bytes - 1] == '\t')) {
81 value[bytes-1] = '\0'; 84 value[bytes - 1] = '\0';
82 bytes=strlen(value); 85 bytes = strlen(value);
83 } 86 }
84 /* bytes should be strlen(value) */ 87 /* bytes should be strlen(value) */
85 if ( value[bytes-1] == '"' ) value[bytes-1] = '\0'; 88 if (value[bytes - 1] == '"')
86 if ( value[0] == '"' ) value++; 89 value[bytes - 1] = '\0';
90 if (value[0] == '"')
91 value++;
87 92
88 /* "trim" param */ 93 /* "trim" param */
89 while ((param[0] == ' ')||(param[0] == '\t')) 94 while ((param[0] == ' ') || (param[0] == '\t'))
90 param++; 95 param++;
91 bytes = strlen(param); 96 bytes = strlen(param);
92 while ((param[bytes-1] == ' ')||(param[bytes-1] == '\t')) { 97 while ((param[bytes - 1] == ' ') || (param[bytes - 1] == '\t')) {
93 param[bytes-1] = '\0'; 98 param[bytes - 1] = '\0';
94 bytes=strlen(param); 99 bytes = strlen(param);
95 } 100 }
96 /* bytes should be strlen(param) */ 101 /* bytes should be strlen(param) */
97 if ( param[bytes-1] == '\"' ) param[bytes-1] = '\0'; 102 if (param[bytes - 1] == '\"')
98 if ( param[0] == '\"' ) param++; 103 param[bytes - 1] = '\0';
104 if (param[0] == '\"')
105 param++;
99 106
100 if ((!param)||(!value)) return; /* failsave */ 107 if ((!param) || (!value))
108 return; /* failsave */
101 109
102 //fprintf(stderr,"\"%s\" -> \"%s\"\n",param,value); 110 // fprintf(stderr,"\"%s\" -> \"%s\"\n",param,value);
103 setnoption(param,value); 111 setnoption(param, value);
104} 112}
105 113
106static void parseformats(char *line) { 114static void parseformats(char *line) {
107 int i; 115 int i;
108 char *tmp = NULL; 116 char *tmp = NULL;
109 117
110 /* read a format line from file, syntax is 118 /* read a format line from file, syntax is
111 FS_XXX = "formatstring" 119 FS_XXX = "formatstring"
112 */ 120 */
113 121
114 while( *line == ' ') line++; 122 while (*line == ' ')
115 123 line++;
116 if( strlen( line ) > TMPSTRSIZE ) return; 124
117 125 if (strlen(line) > TMPSTRSIZE)
118 if( *line != '#') /* allow to comment out the line */ 126 return;
119 for (i = 0; formatstrings[i].formatstr; i++) 127
120 if (!strncasecmp(formatstrings[i].idstring, line, strlen( formatstrings[i].idstring) )) 128 if (*line != '#') /* allow to comment out the line */
121 { 129 for (i = 0; formatstrings[i].formatstr; i++)
122 char *tail = line + strlen( formatstrings[i].idstring); 130 if (!strncasecmp(formatstrings[i].idstring, line,
123 while( *tail==' ' || *tail=='\t') tail++; /* and skip whitespaces */ 131 strlen(formatstrings[i].idstring))) {
124 132 char *tail = line + strlen(formatstrings[i].idstring);
125 if( *tail++ == '=' ) 133 while (*tail == ' ' || *tail == '\t')
126 { 134 tail++; /* and skip whitespaces */
127 while( *tail==' ' || *tail=='\t') tail++; 135
128 if( *(tail++)=='\"' ) 136 if (*tail++ == '=') {
129 { 137 while (*tail == ' ' || *tail == '\t')
130 int j, k = 0, stringends = 0, backslash=0; 138 tail++;
131 for ( j = 0; tail[j] && !stringends; j++) 139 if (*(tail++) == '\"') {
132 { 140 int j, k = 0, stringends = 0, backslash = 0;
133 switch( tail[j] ) { 141 for (j = 0; tail[j] && !stringends; j++) {
134 case '^': 142 switch (tail[j]) {
135 if ( tail[j+1] != '^' ) 143 case '^':
136 tmpstr[k++] = 1; 144 if (tail[j + 1] != '^')
137 break; 145 tmpstr[k++] = 1;
138 case '\\': 146 break;
139 backslash=1-backslash; 147 case '\\':
140 tmpstr[k++] = '\\'; 148 backslash = 1 - backslash;
141 break; 149 tmpstr[k++] = '\\';
142 case '\"': 150 break;
143 if (backslash) k--; else stringends = 1; 151 case '\"':
144 default: 152 if (backslash)
145 tmpstr[k++] = tail[j]; 153 k--;
146 backslash = 0; 154 else
147 } 155 stringends = 1;
148 } 156 default:
149 157 tmpstr[k++] = tail[j];
150 if ( stringends && ( (tmp = (char *)malloc( 1 + j )) != NULL ) ) 158 backslash = 0;
151 {
152 memcpy( tmp, tmpstr, k);
153 tmp[k-1]=0;
154 formatstrings[i].formatstr = tmp;
155 }
156 }
157 } 159 }
158 } 160 }
159 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 }
160} 170}
161 171
162/* UNUSED uncomment if needed 172/* UNUSED uncomment if needed
163static void parseknownhosts(char *line) { 173static void parseknownhosts(char *line) {
164} 174}
165*/ 175*/
166 176
167/* load config file */ 177/* load config file */
168void 178void loadcfg(char *file, int complain, void (*lineparser)(char *)) {
169loadcfg (char *file,int complain, void (*lineparser) (char *))
170{
171 FILE *fh; 179 FILE *fh;
172#define BUFSIZE 4096 180#define BUFSIZE 4096
173 char buf[BUFSIZE]; /* data buffer */ 181 char buf[BUFSIZE]; /* data buffer */
174 char *tildex = NULL, *t; 182 char *tildex = NULL, *t;
175 183
176 /* Check and expand filename then open file */ 184 /* Check and expand filename then open file */
177 if (!file) return; 185 if (!file)
178 tildex = tilde_expand( file ); 186 return;
179 if (!tildex) return; 187 tildex = tilde_expand(file);
180 fh = fopen( tildex, "r" ); 188 if (!tildex)
181 free( tildex ); 189 return;
190 fh = fopen(tildex, "r");
191 free(tildex);
182 192
183 if (!fh) { 193 if (!fh) {
184 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));
185 return; 197 return;
186 } 198 }
187 199
188 while ( fgets( buf, sizeof(buf), fh ) ) { 200 while (fgets(buf, sizeof(buf), fh)) {
189 if( ( t = strchr( buf, '\n' ) ) ) 201 if ((t = strchr(buf, '\n')))
190 *t = 0; 202 *t = 0;
191 lineparser(buf); 203 lineparser(buf);
192 } 204 }
@@ -194,77 +206,62 @@ loadcfg (char *file,int complain, void (*lineparser) (char *))
194 fclose(fh); 206 fclose(fh);
195} 207}
196 208
197void 209void loadconfig(char *file) { loadcfg(file, 1, parsecfg); }
198loadconfig (char *file)
199{
200 loadcfg(file,1,parsecfg);
201}
202 210
203void 211void loadformats(char *file) { loadcfg(file, 0, parseformats); }
204loadformats (char *file)
205{
206 loadcfg(file,0,parseformats);
207}
208 212
209/* get-format-string */ 213/* get-format-string */
210char * 214char *getformatstr(formtstr id) {
211getformatstr (formtstr id)
212{
213 int i; 215 int i;
214 for (i = 0; formatstrings[i].formatstr; i++) 216 for (i = 0; formatstrings[i].formatstr; i++)
215 if (formatstrings[i].id == id) return formatstrings[i].formatstr; 217 if (formatstrings[i].id == id)
218 return formatstrings[i].formatstr;
216 return NULL; 219 return NULL;
217} 220}
218 221
219/* get-string-option, fetches *char-value of variable named by option */ 222/* get-string-option, fetches *char-value of variable named by option */
220char * 223char *getstroption(confopt option) {
221getstroption (confopt option)
222{
223 int i; 224 int i;
224#ifdef DEBUG 225#ifdef DEBUG
225 fprintf(stderr,"getstroption: %d\n",option); 226 fprintf(stderr, "getstroption: %d\n", option);
226#endif 227#endif
227 for (i = 0; configoptions[i].type != CO_NIL; i++) 228 for (i = 0; configoptions[i].type != CO_NIL; i++)
228 if ((configoptions[i].id == option) && (configoptions[i].type == CO_STR)) { 229 if ((configoptions[i].id == option) && (configoptions[i].type == CO_STR)) {
229 if (!configoptions[i].value) 230 if (!configoptions[i].value)
230 return configoptions[i].defaultvalue; 231 return configoptions[i].defaultvalue;
231 else 232 else
232 return configoptions[i].value; 233 return configoptions[i].value;
233 } 234 }
234 return NULL; 235 return NULL;
235} 236}
236 237
237/* set-string-option, puts *char-value to variable named by option */ 238/* set-string-option, puts *char-value to variable named by option */
238void 239void setstroption(confopt option, char *string) {
239setstroption (confopt option, char *string)
240{
241 int i; 240 int i;
242#ifdef DEBUG 241#ifdef DEBUG
243 fprintf(stderr,"setstroption: %d to %s\n",option,string); 242 fprintf(stderr, "setstroption: %d to %s\n", option, string);
244#endif 243#endif
245 for (i = 0; configoptions[i].type != CO_NIL; i++) 244 for (i = 0; configoptions[i].type != CO_NIL; i++)
246 if ((configoptions[i].id == option) && (configoptions[i].type == CO_STR)) { 245 if ((configoptions[i].id == option) && (configoptions[i].type == CO_STR)) {
247 if (configoptions[i].value) 246 if (configoptions[i].value)
248 free(configoptions[i].value); 247 free(configoptions[i].value);
249 if (string) 248 if (string)
250 configoptions[i].value = strdup(string); 249 configoptions[i].value = strdup(string);
251 else 250 else
252 configoptions[i].value = NULL; 251 configoptions[i].value = NULL;
253 if (configoptions[i].localvar.pstr) 252 if (configoptions[i].localvar.pstr)
254 *configoptions[i].localvar.pstr = configoptions[i].value; 253 *configoptions[i].localvar.pstr = configoptions[i].value;
255 } 254 }
256} 255}
257 256
258/* set-named-option, puts string to variable named by name */ 257/* set-named-option, puts string to variable named by name */
259void 258void setnoption(const char *name, char *string) {
260setnoption (const char *name, char *string)
261{
262 int i; 259 int i;
263#ifdef DEBUG 260#ifdef DEBUG
264 fprintf(stderr,"setstrnoption: %s to %s\n",name,string); 261 fprintf(stderr, "setstrnoption: %s to %s\n", name, string);
265#endif 262#endif
266 for (i = 0; configoptions[i].type != CO_NIL; i++) 263 for (i = 0; configoptions[i].type != CO_NIL; i++)
267 if (!strcmp(configoptions[i].varname,name)) { 264 if (!strcmp(configoptions[i].varname, name)) {
268 if (configoptions[i].type == CO_STR) { 265 if (configoptions[i].type == CO_STR) {
269 if (configoptions[i].value) 266 if (configoptions[i].value)
270 free(configoptions[i].value); 267 free(configoptions[i].value);
@@ -273,265 +270,288 @@ setnoption (const char *name, char *string)
273 else 270 else
274 configoptions[i].value = NULL; 271 configoptions[i].value = NULL;
275 if (configoptions[i].localvar.pstr) 272 if (configoptions[i].localvar.pstr)
276 *configoptions[i].localvar.pstr = configoptions[i].value; 273 *configoptions[i].localvar.pstr = configoptions[i].value;
277 } else if (configoptions[i].type == CO_INT) { 274 } else if (configoptions[i].type == CO_INT) {
278 configoptions[i].value = (char *)(uintptr_t)atoi(string); 275 configoptions[i].value = (char *)(uintptr_t)atoi(string);
279 if (configoptions[i].localvar.pint) 276 if (configoptions[i].localvar.pint)
280 *configoptions[i].localvar.pint = (uintptr_t)configoptions[i].value; 277 *configoptions[i].localvar.pint = (uintptr_t)configoptions[i].value;
281 } 278 }
282 } 279 }
283} 280}
284 281
285/* get-integer-option, fetches int-value of variable named by option */ 282/* get-integer-option, fetches int-value of variable named by option */
286int 283int getintoption(confopt option) {
287getintoption (confopt option)
288{
289 int i; 284 int i;
290#ifdef DEBUG 285#ifdef DEBUG
291 fprintf(stderr,"getintoption: %d\n",option); 286 fprintf(stderr, "getintoption: %d\n", option);
292#endif 287#endif
293 for (i = 0; configoptions[i].type != CO_NIL; i++) 288 for (i = 0; configoptions[i].type != CO_NIL; i++)
294 if ((configoptions[i].id == option) && (configoptions[i].type == CO_INT)) { 289 if ((configoptions[i].id == option) && (configoptions[i].type == CO_INT)) {
295 if ((intptr_t)configoptions[i].value == -1) 290 if ((intptr_t)configoptions[i].value == -1)
296 return (intptr_t) configoptions[i].defaultvalue; 291 return (intptr_t)configoptions[i].defaultvalue;
297 else 292 else
298 return (intptr_t) configoptions[i].value; 293 return (intptr_t)configoptions[i].value;
299 } 294 }
300 return 0; 295 return 0;
301} 296}
302 297
303/* set-integer-option, puts int-value to variable named by option */ 298/* set-integer-option, puts int-value to variable named by option */
304void 299void setintoption(confopt option, int value) {
305setintoption (confopt option, int value)
306{
307 int i; 300 int i;
308#ifdef DEBUG 301#ifdef DEBUG
309 fprintf(stderr,"setintoption: %d to %d\n",option,value); 302 fprintf(stderr, "setintoption: %d to %d\n", option, value);
310#endif 303#endif
311 for (i = 0; configoptions[i].type != CO_NIL; i++) 304 for (i = 0; configoptions[i].type != CO_NIL; i++)
312 if ((configoptions[i].id == option) && (configoptions[i].type == CO_INT)) { 305 if ((configoptions[i].id == option) && (configoptions[i].type == CO_INT)) {
313 configoptions[i].value = (char *)(uintptr_t)value; 306 configoptions[i].value = (char *)(uintptr_t)value;
314 if (configoptions[i].localvar.pint) 307 if (configoptions[i].localvar.pint)
315 *configoptions[i].localvar.pint = (uintptr_t)configoptions[i].value; 308 *configoptions[i].localvar.pint = (uintptr_t)configoptions[i].value;
316 } 309 }
317} 310}
318 311
319int quitrequest = 0; 312int quitrequest = 0;
320 313
321/* cleanup-hook, for SIGINT */ 314/* cleanup-hook, for SIGINT */
322void 315void cleanup(int signal) {
323cleanup (int signal) 316 if (signal == SIGINT) {
324{ 317 switch (quitrequest >> 2) {
325 if( signal == SIGINT ) { 318 case 0:
326 switch( quitrequest >> 2 ) { 319 flushout();
327 case 0: 320 writeout(" Press Ctrl+C twice now to confirm ");
328 flushout( ); 321 showout();
329 writeout( " Press Ctrl+C twice now to confirm "); 322 quitrequest += 4;
330 showout( ); 323 return;
331 quitrequest+=4; 324 break;
332 return; 325 case 1:
333 break; 326 flushout();
334 case 1: 327 writeout(" Press Ctrl+C twice now to confirm ");
335 flushout( ); 328 writeout(" Press Ctrl+C once now to confirm ");
336 writeout( " Press Ctrl+C twice now to confirm "); 329 showout();
337 writeout( " Press Ctrl+C once now to confirm "); 330 quitrequest += 4;
338 showout( ); 331 return;
339 quitrequest+=4; 332 break;
340 return; 333 default:
341 break; 334 break;
342 default: 335 }
343 break;
344 }
345 } 336 }
346 /* restore terminal state */ 337 /* restore terminal state */
347 exitui (); 338 exitui();
348 /* clear userlist */ 339 /* clear userlist */
349 ul_clear (); 340 ul_clear();
350 vc_disconnect(); 341 vc_disconnect();
351 342
352 /* inform user if we where killed by signal */ 343 /* inform user if we where killed by signal */
353 if (signal > 1) 344 if (signal > 1) {
354 { 345 fprintf(stderr, "vchat-client: terminated with signal %d.\n", signal);
355 fprintf (stderr, "vchat-client: terminated with signal %d.\n", signal); 346 } else if (errstr[0])
356 } else if (errstr[0]) 347 fputs(errstr, stderr);
357 fputs (errstr, stderr);
358 /* end of story */ 348 /* end of story */
359 exit (0); 349 exit(0);
360} 350}
361 351
362static int oldseconds = 0; 352static int oldseconds = 0;
363 353
364void calleverysecond( void ) { 354void calleverysecond(void) {
365 /* timetriggered execution, don't rely on being called every 1000us */ 355 /* timetriggered execution, don't rely on being called every 1000us */
366 /* rather see it as a chance for being called 9 times in 10 seconds */ 356 /* rather see it as a chance for being called 9 times in 10 seconds */
367 /* so check time() */ 357 /* so check time() */
368 time_t now = time( NULL ); 358 time_t now = time(NULL);
369 struct tm *mytime = localtime( &now ); 359 struct tm *mytime = localtime(&now);
370 if( mytime->tm_sec < oldseconds ) { 360 if (mytime->tm_sec < oldseconds) {
371 consoleline( NULL ); 361 consoleline(NULL);
372 } 362 }
373 oldseconds = mytime->tm_sec; 363 oldseconds = mytime->tm_sec;
374 364
375 if(quitrequest) 365 if (quitrequest)
376 quitrequest--; 366 quitrequest--;
377 if(outputcountdown && !--outputcountdown) 367 if (outputcountdown && !--outputcountdown)
378 hideout( ); 368 hideout();
379 if( reconnect_time && ( time( NULL ) > reconnect_time ) ) 369 if (reconnect_time && (time(NULL) > reconnect_time))
380 status = 0; 370 status = 0;
381} 371}
382 372
383/* this function is called in the master loop */ 373/* this function is called in the master loop */
384void 374void eventloop(void) {
385eventloop (void) 375 int poll_result = vc_poll(1 /* second timeout */);
386{
387 int poll_result = vc_poll( 1 /* second timeout */ );
388 376
389 switch (poll_result) { 377 switch (poll_result) {
390 case -1: 378 case -1:
391 /* EINTR is most likely a SIGWINCH - ignore for now */ 379 /* EINTR is most likely a SIGWINCH - ignore for now */
392 if (errno != EINTR) 380 if (errno != EINTR) {
393 { 381 snprintf(tmpstr, TMPSTRSIZE, "Select fails, %s.", strerror(errno));
394 snprintf (tmpstr, TMPSTRSIZE, "Select fails, %s.", strerror(errno)); 382 strncpy(errstr, tmpstr, TMPSTRSIZE - 2);
395 strncpy(errstr,tmpstr,TMPSTRSIZE-2); 383 errstr[TMPSTRSIZE - 2] = '\0';
396 errstr[TMPSTRSIZE-2] = '\0'; 384 strcat(errstr, "\n");
397 strcat(errstr,"\n"); 385 writecf(FS_ERR, tmpstr);
398 writecf (FS_ERR,tmpstr); 386 /* see this as an error condition and bail out */
399 /* see this as an error condition and bail out */ 387 status = 0;
400 status = 0; 388 }
401 } 389 break;
402 break;
403 case 0: 390 case 0:
404 /* time out reached */ 391 /* time out reached */
405 calleverysecond(); 392 calleverysecond();
406 break; 393 break;
407 default: 394 default:
408 /* something to read from user & we're logged in or have a cert? */ 395 /* something to read from user & we're logged in or have a cert? */
409 if (poll_result & 1) 396 if (poll_result & 1)
410 userinput (); 397 userinput();
411 398
412 /* something to read from server? */ 399 /* something to read from server? */
413 if (poll_result & 2) 400 if (poll_result & 2)
414 vc_receive (); 401 vc_receive();
415 break; 402 break;
416 } 403 }
417} 404}
418 405
419void usage( char *name) { 406void usage(char *name) {
420 printf ("usage: %s [-C config-file] [-F formats] [-l] [-z] [-s host] [-p port] [-c channel] [-n nickname]\n",name); 407 printf("usage: %s [-C config-file] [-F formats] [-l] [-z] [-s host] [-p "
421 puts (" -C load a second config-file, overriding the first one"); 408 "port] [-c channel] [-n nickname]\n",
422 puts (" -F load format strings (skins) from this file"); 409 name);
423 puts (" -l local connect (no SSL)"); 410 puts(" -C load a second config-file, overriding the first one");
424 puts (" -z don't use certificate files"); 411 puts(" -F load format strings (skins) from this file");
425 printf (" -s set server (default \"%s\")\n",getstroption(CF_SERVERHOST)); 412 puts(" -l local connect (no SSL)");
426 printf (" -p set port (default %s)\n",getstroption(CF_SERVERPORT)); 413 puts(" -z don't use certificate files");
427 printf (" -c set channel (default %d)\n",getintoption(CF_CHANNEL)); 414 printf(" -s set server (default \"%s\")\n", getstroption(CF_SERVERHOST));
428 if (own_nick_get()) 415 printf(" -p set port (default %s)\n", getstroption(CF_SERVERPORT));
429 printf(" -n set nickname (default \"%s\")\n",own_nick_get()); 416 printf(" -c set channel (default %d)\n", getintoption(CF_CHANNEL));
430 else 417 if (own_nick_get())
431 puts (" -n set nickname"); 418 printf(" -n set nickname (default \"%s\")\n", own_nick_get());
432 printf (" -f set from (default \"%s\")\n",getstroption(CF_FROM)); 419 else
433 puts (" -h gives this help"); 420 puts(" -n set nickname");
434 puts (" -v show module versions"); 421 printf(" -f set from (default \"%s\")\n", getstroption(CF_FROM));
422 puts(" -h gives this help");
423 puts(" -v show module versions");
435} 424}
436 425
437void versions() { 426void versions() {
438 puts (vchat_cl_version); 427 puts(vchat_cl_version);
439 puts (vchat_ui_version); 428 puts(vchat_ui_version);
440 puts (vchat_io_version); 429 puts(vchat_io_version);
441 puts (vchat_us_version); 430 puts(vchat_us_version);
442 puts (vchat_cm_version); 431 puts(vchat_cm_version);
443 puts (vchat_tls_version); 432 puts(vchat_tls_version);
444 puts (vchat_tls_version_external); 433 puts(vchat_tls_version_external);
445} 434}
446 435
447/* main - d'oh */ 436/* main - d'oh */
448int 437int main(int argc, char **argv) {
449main (int argc, char **argv)
450{
451 int pchar; 438 int pchar;
452 int cmdsunparsed = 1; 439 int cmdsunparsed = 1;
453 440
454 setlocale(LC_ALL,""); 441 setlocale(LC_ALL, "");
455 442
456 loadconfig (GLOBAL_CONFIG_FILE); 443 loadconfig(GLOBAL_CONFIG_FILE);
457 loadconfig (getstroption (CF_CONFIGFILE)); 444 loadconfig(getstroption(CF_CONFIGFILE));
458 445
459 /* make SSL version used visible */ 446 /* make SSL version used visible */
460 vchat_tls_get_version_external(); 447 vchat_tls_get_version_external();
461 448
462 /* parse commandline */ 449 /* parse commandline */
463 while (cmdsunparsed) { 450 while (cmdsunparsed) {
464 pchar = getopt(argc,argv,"C:F:lzs:p:c:n:f:kKL:hv"); 451 pchar = getopt(argc, argv, "C:F:lzs:p:c:n:f:kKL:hv");
465#ifdef DEBUG 452#ifdef DEBUG
466 fprintf(stderr,"parse commandline: %d ('%c'): %s\n",pchar,pchar,optarg); 453 fprintf(stderr, "parse commandline: %d ('%c'): %s\n", pchar, pchar, optarg);
467#endif 454#endif
468 455
469 switch (pchar) { 456 switch (pchar) {
470 case -1 : cmdsunparsed = 0; break; 457 case -1:
471 case 'C': loadconfig(optarg); break; 458 cmdsunparsed = 0;
472 case 'F': setstroption(CF_FORMFILE,optarg); break; 459 break;
473 case 'l': setintoption(CF_USESSL,0); break; 460 case 'C':
474 case 'z': setintoption(CF_USECERT,0); break; 461 loadconfig(optarg);
475 case 's': setstroption(CF_SERVERHOST,optarg); break; 462 break;
476 case 'p': setstroption(CF_SERVERPORT,optarg); break; 463 case 'F':
477 case 'c': setintoption(CF_CHANNEL,strtol(optarg,NULL,10)); break; 464 setstroption(CF_FORMFILE, optarg);
478 case 'n': own_nick_set(optarg); break; 465 break;
479 case 'f': setstroption(CF_FROM,optarg); break; 466 case 'l':
480 case 'h': usage(argv[0]); exit(0); break; 467 setintoption(CF_USESSL, 0);
481 case 'v': versions(); exit(0); break; 468 break;
482 default : usage(argv[0]); exit(1); 469 case 'z':
483 } 470 setintoption(CF_USECERT, 0);
471 break;
472 case 's':
473 setstroption(CF_SERVERHOST, optarg);
474 break;
475 case 'p':
476 setstroption(CF_SERVERPORT, optarg);
477 break;
478 case 'c':
479 setintoption(CF_CHANNEL, strtol(optarg, NULL, 10));
480 break;
481 case 'n':
482 own_nick_set(optarg);
483 break;
484 case 'f':
485 setstroption(CF_FROM, optarg);
486 break;
487 case 'h':
488 usage(argv[0]);
489 exit(0);
490 break;
491 case 'v':
492 versions();
493 exit(0);
494 break;
495 default:
496 usage(argv[0]);
497 exit(1);
498 }
484 } 499 }
485 500
486 if (optind < argc) { usage(argv[0]); exit(1); } 501 if (optind < argc) {
502 usage(argv[0]);
503 exit(1);
504 }
487 505
488 loadformats(GLOBAL_FORMAT_FILE); 506 loadformats(GLOBAL_FORMAT_FILE);
489 loadformats(getstroption (CF_FORMFILE)); 507 loadformats(getstroption(CF_FORMFILE));
490 508
491 /* install signal handler */ 509 /* install signal handler */
492 signal (SIGINT, cleanup); 510 signal(SIGINT, cleanup);
493 signal (SIGHUP, cleanup); 511 signal(SIGHUP, cleanup);
494 signal (SIGTERM, cleanup); 512 signal(SIGTERM, cleanup);
495 signal (SIGQUIT, SIG_IGN); 513 signal(SIGQUIT, SIG_IGN);
496 514
497 /* initialize userinterface */ 515 /* initialize userinterface */
498 initui (); 516 initui();
499 517
500 while( status ) { 518 while (status) {
501 /* attempt connection */ 519 /* attempt connection */
502 if (vc_connect (getstroption(CF_SERVERHOST), getstroption(CF_SERVERPORT))) { 520 if (vc_connect(getstroption(CF_SERVERHOST), getstroption(CF_SERVERPORT))) {
503 snprintf (tmpstr, TMPSTRSIZE, "Could not connect to server, %s.", strerror(errno)); 521 snprintf(tmpstr, TMPSTRSIZE, "Could not connect to server, %s.",
504 strncpy(errstr,tmpstr,TMPSTRSIZE-2); 522 strerror(errno));
505 errstr[TMPSTRSIZE-2] = '\0'; 523 strncpy(errstr, tmpstr, TMPSTRSIZE - 2);
506 strcat(errstr,"\n"); 524 errstr[TMPSTRSIZE - 2] = '\0';
507 writecf (FS_ERR,tmpstr); 525 strcat(errstr, "\n");
508 526 writecf(FS_ERR, tmpstr);
509 if( getintoption( CF_AUTORECONN ) ) { 527
510 snprintf (tmpstr, TMPSTRSIZE, "reconnecting in %d seconds", reconnect_delay ); 528 if (getintoption(CF_AUTORECONN)) {
511 writecf (FS_ERR, tmpstr); 529 snprintf(tmpstr, TMPSTRSIZE, "reconnecting in %d seconds",
512 reconnect_delay = ( reconnect_delay * 15 ) / 10; 530 reconnect_delay);
513 reconnect_time = time( NULL ) + reconnect_delay; 531 writecf(FS_ERR, tmpstr);
532 reconnect_delay = (reconnect_delay * 15) / 10;
533 reconnect_time = time(NULL) + reconnect_delay;
514 } else 534 } else
515 status = 0; 535 status = 0;
516 } else { 536 } else {
517 /* reset reconnect delay */ 537 /* reset reconnect delay */
518 reconnect_delay = 6; 538 reconnect_delay = 6;
519 reconnect_time = 0; 539 reconnect_time = 0;
520 } 540 }
521 541
522 while (status) 542 while (status)
523 eventloop (); 543 eventloop();
524 544
525 /* sanely close connection to server */ 545 /* sanely close connection to server */
526 vc_disconnect (); 546 vc_disconnect();
527 547
528 if( !ownquit && ( getintoption( CF_AUTORECONN ) || wantreconnect ) ) 548 if (!ownquit && (getintoption(CF_AUTORECONN) || wantreconnect))
529 status = 1; 549 status = 1;
530 550
531 wantreconnect = 0; 551 wantreconnect = 0;
532 } 552 }
533 553
534 /* call cleanup-hook without signal */ 554 /* call cleanup-hook without signal */
535 cleanup (0); 555 cleanup(0);
536 return 0; 556 return 0;
537} 557}
diff --git a/vchat-commands.c b/vchat-commands.c
index 8094a7c..9aa3abd 100755
--- a/vchat-commands.c
+++ b/vchat-commands.c
@@ -10,27 +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-connection.h"
29#include "vchat-help.h" 29#include "vchat-help.h"
30#include "vchat-user.h" 30#include "vchat-user.h"
31#include "vchat.h"
31 32
32/* version of this module */ 33/* version of this module */
33const char *vchat_cm_version = "vchat-commands.c $Id$"; 34const char *vchat_cm_version =
35 "vchat-commands.c $Id$";
34 36
35/* from vchat-client.c */ 37/* from vchat-client.c */
36extern int ownquit; 38extern int ownquit;
@@ -39,45 +41,45 @@ extern int status;
39 41
40/* our "/command " table */ 42/* our "/command " table */
41enum { 43enum {
42COMMAND_VERSION, 44 COMMAND_VERSION,
43COMMAND_FILTERS, 45 COMMAND_FILTERS,
44COMMAND_LSFLT, 46 COMMAND_LSFLT,
45COMMAND_RMFLT, 47 COMMAND_RMFLT,
46COMMAND_CLFLT, 48 COMMAND_CLFLT,
47COMMAND_HELP, 49 COMMAND_HELP,
48COMMAND_FORMAT, 50 COMMAND_FORMAT,
49COMMAND_KEYS, 51 COMMAND_KEYS,
50COMMAND_QUIT, 52 COMMAND_QUIT,
51COMMAND_USER, 53 COMMAND_USER,
52COMMAND_DICT, 54 COMMAND_DICT,
53COMMAND_FLT, 55 COMMAND_FLT,
54COMMAND_PM, 56 COMMAND_PM,
55COMMAND_ACTION, 57 COMMAND_ACTION,
56COMMAND_PMSHORT, 58 COMMAND_PMSHORT,
57COMMAND_QUERY, 59 COMMAND_QUERY,
58COMMAND_QUITSHORT, 60 COMMAND_QUITSHORT,
59COMMAND_PLAIN, 61 COMMAND_PLAIN,
60COMMAND_RECONNECT, 62 COMMAND_RECONNECT,
61COMMAND_NONE 63 COMMAND_NONE
62}; 64};
63 65
64static void command_quit ( char *tail); 66static void command_quit(char *tail);
65static void command_user ( char *tail); 67static void command_user(char *tail);
66static void command_pm ( char *tail); 68static void command_pm(char *tail);
67static void command_action ( char *tail); 69static void command_action(char *tail);
68static void command_help ( char *tail); 70static void command_help(char *tail);
69static void command_format ( char *tail); 71static void command_format(char *tail);
70static void command_flt ( char *tail); 72static void command_flt(char *tail);
71static void command_lsflt ( char *tail); 73static void command_lsflt(char *tail);
72static void command_clflt ( char *tail); 74static void command_clflt(char *tail);
73static void command_rmflt ( char *tail); 75static void command_rmflt(char *tail);
74 void command_version ( char *tail); 76void command_version(char *tail);
75static void command_none ( char *line); 77static void command_none(char *line);
76static void command_query ( char *tail); 78static void command_query(char *tail);
77static void command_reconnect ( char *tail); 79static void command_reconnect(char *tail);
78static void command_dict ( char *tail); 80static void command_dict(char *tail);
79 81
80static void output_default ( char *tail); 82static void output_default(char *tail);
81 83
82/* commandentry defined in vchat.h */ 84/* commandentry defined in vchat.h */
83 85
@@ -106,9 +108,7 @@ commandtable[] = {
106}; 108};
107 109
108/* parse "/command" */ 110/* parse "/command" */
109static int 111static int translatecommand(char **cmd) {
110translatecommand( char **cmd)
111{
112 int result; 112 int result;
113 int cut = 0; 113 int cut = 0;
114 int maxcut = 0; 114 int maxcut = 0;
@@ -116,214 +116,215 @@ translatecommand( char **cmd)
116 /* We do only want to allow Command abbrevation to 116 /* We do only want to allow Command abbrevation to
117 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 */
118 118
119 while( (*cmd)[maxcut] && ((*cmd)[maxcut] != 0x20) && ((*cmd)[maxcut] != '\n')) maxcut++; 119 while ((*cmd)[maxcut] && ((*cmd)[maxcut] != 0x20) && ((*cmd)[maxcut] != '\n'))
120 if( maxcut ) maxcut--; 120 maxcut++;
121 if (maxcut)
122 maxcut--;
121 123
122 /* Repeatedly scan command table for command, with growing abbrevation cut off */ 124 /* Repeatedly scan command table for command, with growing abbrevation cut off
125 */
123 do { 126 do {
124 /* Looks ugly, needs rewrite for better understanding */ 127 /* Looks ugly, needs rewrite for better understanding */
125 for( result = 0; 128 for (result = 0;
126 (result != COMMAND_NONE) && 129 (result != COMMAND_NONE) &&
127 (strncasecmp(*cmd, commandtable[result].name, commandtable[result].len - 130 (strncasecmp(
128 ((commandtable[result].len - maxcut - cut > 0) ? cut : 0))); 131 *cmd, commandtable[result].name,
129 result++); 132 commandtable[result].len -
130 } 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));
131 138
132 /* Just leave the tail... */ 139 /* Just leave the tail... */
133 (*cmd) += commandtable[result].len; 140 (*cmd) += commandtable[result].len;
134 141
135 /* ... whose start may be affected by abbrevation */ 142 /* ... whose start may be affected by abbrevation */
136 if( commandtable[result].number != COMMAND_NONE ) 143 if (commandtable[result].number != COMMAND_NONE)
137 (*cmd) -= cut; 144 (*cmd) -= cut;
138 145
139 return result; 146 return result;
140} 147}
141 148
142/* handle thought */ 149/* handle thought */
143static void 150static void dothink(const char *tail, const char nice) {
144dothink(const char *tail, const char nice ) 151 while (*tail == ' ')
145{ 152 tail++;
146 while( *tail == ' ' ) tail++;
147 153
148 /* send users message to server */ 154 /* send users message to server */
149 snprintf (tmpstr, TMPSTRSIZE, ".%c %s", nice, tail); 155 snprintf(tmpstr, TMPSTRSIZE, ".%c %s", nice, tail);
150 vc_sendmessage (tmpstr); 156 vc_sendmessage(tmpstr);
151 157
152 /* show action in channel window */ 158 /* show action in channel window */
153 snprintf (tmpstr, TMPSTRSIZE, nice == 'O' ? getformatstr(FS_TXPUBNTHOUGHT) : getformatstr(FS_TXPUBTHOUGHT), tail); 159 snprintf(tmpstr, TMPSTRSIZE,
154 writechan (tmpstr); 160 nice == 'O' ? getformatstr(FS_TXPUBNTHOUGHT)
161 : getformatstr(FS_TXPUBTHOUGHT),
162 tail);
163 writechan(tmpstr);
155} 164}
156 165
157
158/* handle action */ 166/* handle action */
159static void 167static void doaction(const char *tail) {
160doaction(const char *tail ) 168 while (*tail == ' ')
161{ 169 tail++;
162 while( *tail == ' ' ) tail++; 170
163 171 if (*tail) {
164 if( *tail ) { 172 /* send users message to server */
165 /* send users message to server */ 173 snprintf(tmpstr, TMPSTRSIZE, ".a %s", tail);
166 snprintf (tmpstr, TMPSTRSIZE, ".a %s", tail); 174 vc_sendmessage(tmpstr);
167 vc_sendmessage (tmpstr); 175
168 176 /* show action in channel window */
169 /* show action in channel window */ 177 snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_TXPUBACTION), own_nick_get(),
170 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_TXPUBACTION), own_nick_get(), tail); 178 tail);
171 writechan (tmpstr); 179 writechan(tmpstr);
172 } else { 180 } else {
173 /* missing action */ 181 /* missing action */
174 msgout( " You do nothing. " ); 182 msgout(" You do nothing. ");
175 } 183 }
176} 184}
177 185
178/* handle private message outgoing */ 186/* handle private message outgoing */
179static void 187static void privatemessagetx(char *tail) {
180privatemessagetx (char *tail ) {
181 char *mesg; 188 char *mesg;
182 189
183 /* find nick */ 190 /* find nick */
184 while( *tail==' ') tail++; 191 while (*tail == ' ')
192 tail++;
185 193
186 /* find message */ 194 /* find message */
187 mesg = tail; 195 mesg = tail;
188 while ( *mesg && *mesg!=' ') mesg++; 196 while (*mesg && *mesg != ' ')
197 mesg++;
189 198
190 /* check for nick && message */ 199 /* check for nick && message */
191 if(*tail && *mesg) { 200 if (*tail && *mesg) {
192 201
193 /* terminate nick, move to rel start */ 202 /* terminate nick, move to rel start */
194 *mesg++ = '\0'; 203 *mesg++ = '\0';
195 204
196 /* form message and send to server */ 205 /* form message and send to server */
197 snprintf (tmpstr, TMPSTRSIZE, ".m %s %s", tail, mesg); 206 snprintf(tmpstr, TMPSTRSIZE, ".m %s %s", tail, mesg);
198 vc_sendmessage (tmpstr); 207 vc_sendmessage(tmpstr);
199 208
200 /* show message in private window */ 209 /* show message in private window */
201 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_TXPRIVMSG), tail, mesg); 210 snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_TXPRIVMSG), tail, mesg);
202 writepriv (tmpstr, 0); 211 writepriv(tmpstr, 0);
203 212
204 /* note we messaged someone */ 213 /* note we messaged someone */
205 ul_private_action(tail); 214 ul_private_action(tail);
206 215
207 } else { 216 } else {
208 /* Bump user to fill in missing parts */ 217 /* Bump user to fill in missing parts */
209 msgout( *tail ? " Won't send empty message. ":" Recipient missing. " ); 218 msgout(*tail ? " Won't send empty message. " : " Recipient missing. ");
210 } 219 }
211} 220}
212 221
213/* handle line entered by user */ 222/* handle line entered by user */
214void 223void handleline(char *line) {
215handleline (char *line)
216{
217#ifdef DEBUG 224#ifdef DEBUG
218 /* debugging? log users input! */ 225 /* debugging? log users input! */
219 fprintf (stderr, "=| %s\n", line); 226 fprintf(stderr, "=| %s\n", line);
220#endif 227#endif
221 228
222 switch ( line[0] ) 229 switch (line[0]) {
223 {
224 case '.': 230 case '.':
225 switch ( line[1] ) { 231 switch (line[1]) {
226 case 'm': /* sending a private message? */ 232 case 'm': /* sending a private message? */
227 privatemessagetx( line+2 ); 233 privatemessagetx(line + 2);
228 break; 234 break;
229 case 'a': /* Do an action */ 235 case 'a': /* Do an action */
230 doaction( line+2 ); 236 doaction(line + 2);
231 break; 237 break;
232 case '.': 238 case '.':
233 /* .. on start of line is public */ 239 /* .. on start of line is public */
234 if( line[2] != 'm' ) { 240 if (line[2] != 'm') {
235 output_default( line ); 241 output_default(line);
236 } else { 242 } else {
237 /* oopsi, "..m " detected */ 243 /* oopsi, "..m " detected */
238 /* dunno what to do */ 244 /* dunno what to do */
239 flushout( ); 245 flushout();
240 writeout("? You probably misstyped ?"); 246 writeout("? You probably misstyped ?");
241 writeout(" "); 247 writeout(" ");
242 writeout(line ); 248 writeout(line);
243 showout( ); 249 showout();
244 }
245 break;
246 case 'o':
247 case 'O':
248 dothink( line + 2, line[1] );
249 break;
250 case 'x':
251 /* inform vchat-client, that the following connection
252 drop was intentional */
253 ownquit = 1; /* fallthrough intended */
254 default:
255 /* generic server command, send to server, show to user */
256 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_COMMAND), line);
257 writechan (tmpstr);
258 vc_sendmessage (line);
259 break;
260 } 250 }
261 break; 251 break;
262 case '/': 252 case 'o':
263 line++; 253 case 'O':
264 commandtable[translatecommand(&line)].handler(line); 254 dothink(line + 2, line[1]);
265 break; 255 break;
266 default: 256 case 'x':
267 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);
268 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;
269 } 275 }
270} 276}
271 277
272static void 278static void output_default(char *line) {
273output_default(char *line ) { 279 /* prepare for output on display */
274 /* prepare for output on display */ 280 snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_TXPUBMSG), own_nick_get(), line);
275 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_TXPUBMSG), own_nick_get(), line);
276 281
277 /* send original line to server */ 282 /* send original line to server */
278 vc_sendmessage (line); 283 vc_sendmessage(line);
279 284
280 /* output message to channel window */ 285 /* output message to channel window */
281 writechan (tmpstr); 286 writechan(tmpstr);
282} 287}
283 288
284/* handle a "/user " request */ 289/* handle a "/user " request */
285static void 290static void command_user(char *tail) {
286command_user(char *tail) 291 while (*tail == ' ')
287{ 292 tail++;
288 while( *tail == ' ') tail++; 293 if (*tail) {
289 if( *tail ) { 294 char *out = ul_match_user(tail);
290 char * out = ul_match_user( tail); 295 if (*out) {
291 if( *out ) { 296 snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_USMATCH), tail, out);
292 snprintf( tmpstr, TMPSTRSIZE, getformatstr(FS_USMATCH), tail, out); 297 } else {
293 } else { 298 snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_ERR),
294 snprintf( tmpstr, TMPSTRSIZE, getformatstr(FS_ERR), " No user matched that regex. "); 299 " No user matched that regex. ");
295 } 300 }
296 } else { 301 } else {
297 snprintf( tmpstr, TMPSTRSIZE, getformatstr(FS_ERR), " Which user? "); 302 snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_ERR), " Which user? ");
298 } 303 }
299 msgout( tmpstr ); 304 msgout(tmpstr);
300} 305}
301 306
302/* handle a "/msg " request */ 307/* handle a "/msg " request */
303static void 308static void command_pm(char *tail) { privatemessagetx(tail); }
304command_pm (char *tail)
305{
306 privatemessagetx( tail );
307}
308 309
309static void 310static void command_format(char *line) {
310command_format(char *line) {
311 struct stat testexist; 311 struct stat testexist;
312 char * tildex = NULL; 312 char *tildex = NULL;
313 313
314 flushout(); 314 flushout();
315 while( *line==' ') line++; 315 while (*line == ' ')
316 if(line) { 316 line++;
317 tildex = tilde_expand( line ); 317 if (line) {
318 if(tildex && !stat(tildex, &testexist )) 318 tildex = tilde_expand(line);
319 if (tildex && !stat(tildex, &testexist))
319 loadformats(tildex); 320 loadformats(tildex);
320 else { 321 else {
321#define BUFSIZE 4096 322#define BUFSIZE 4096
322 char buf[BUFSIZE]; 323 char buf[BUFSIZE];
323 snprintf( buf, BUFSIZE, "~/.vchat/sample-%s.fmt", line ); 324 snprintf(buf, BUFSIZE, "~/.vchat/sample-%s.fmt", line);
324 free(tildex); 325 free(tildex);
325 tildex = tilde_expand( line ); 326 tildex = tilde_expand(line);
326 if(tildex && !stat(tildex, &testexist )) 327 if (tildex && !stat(tildex, &testexist))
327 loadformats(tildex); 328 loadformats(tildex);
328 } 329 }
329 writeout(" Sort of done. "); 330 writeout(" Sort of done. ");
@@ -335,92 +336,87 @@ command_format(char *line) {
335} 336}
336 337
337/* handle a help request */ 338/* handle a help request */
338static void 339static void command_help(char *line) {
339command_help (char *line) { 340 flushout();
340 flushout( ); 341 while (*line == ' ')
341 while( *line==' ') line++; 342 line++;
342 if( *line ) { /* Get help on command */ 343 if (*line) { /* Get help on command */
343 int i; 344 int i;
344 if( ( i = translatecommand( &line ) ) != COMMAND_NONE ) { 345 if ((i = translatecommand(&line)) != COMMAND_NONE) {
345 snprintf( tmpstr, TMPSTRSIZE, "Help on command: %s", commandtable[i].name); 346 snprintf(tmpstr, TMPSTRSIZE, "Help on command: %s", commandtable[i].name);
346 writeout( tmpstr ); 347 writeout(tmpstr);
347 writeout(" "); 348 writeout(" ");
348 if( commandtable[i].short_help && !commandtable[i].help ) 349 if (commandtable[i].short_help && !commandtable[i].help)
349 writeout(commandtable[i].short_help ); 350 writeout(commandtable[i].short_help);
350 line = commandtable[i].help; 351 line = commandtable[i].help;
351 if( line ) { 352 if (line) {
352 while( *line ) { 353 while (*line) {
353 char *tmp = tmpstr; 354 char *tmp = tmpstr;
354 while( *line && (*line != '\n') ) 355 while (*line && (*line != '\n'))
355 *tmp++ = *line++; 356 *tmp++ = *line++;
356 *tmp = '\0'; if( *line == '\n') line++; 357 *tmp = '\0';
357 writeout ( tmpstr ); 358 if (*line == '\n')
358 } 359 line++;
359 } 360 writeout(tmpstr);
360 } else { 361 }
361 command_help( " " );
362 }
363 } else { /* Get overall help */
364 int i;
365 for( i = 0; commandtable[i].number != COMMAND_NONE; i++ ) {
366 if( commandtable[i].short_help )
367 writeout( commandtable[i].short_help );
368 } 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 }
369 } 372 }
370 showout(); 373 showout();
371} 374}
372 375
373/* handle an unknown command */ 376/* handle an unknown command */
374static void 377static void command_none(char *line) {
375command_none(char *line) { 378 snprintf(tmpstr, TMPSTRSIZE, " Unknown client command: %s ", line);
376 snprintf(tmpstr, TMPSTRSIZE, " Unknown client command: %s ", line); 379 msgout(tmpstr);
377 msgout(tmpstr);
378} 380}
379 381
380/* handle a "/flt " request */ 382/* handle a "/flt " request */
381static void 383static void command_flt(char *tail) {
382command_flt(char *tail){
383 char colour; 384 char colour;
384 while(*tail==' ') tail++; 385 while (*tail == ' ')
386 tail++;
385 colour = *tail++; 387 colour = *tail++;
386 while( colour && *tail == ' ') tail++; 388 while (colour && *tail == ' ')
387 if( colour && *tail) { 389 tail++;
388 addfilter( colour, tail); 390 if (colour && *tail) {
391 addfilter(colour, tail);
389 } 392 }
390} 393}
391 394
392/* handle a "/clflt " request */ 395/* handle a "/clflt " request */
393static void 396static void command_clflt(char *tail) {
394command_clflt (char *tail) { 397 while (*tail == ' ')
395 while( *tail == ' ') tail++; 398 tail++;
396 clearfilters( *tail ); 399 clearfilters(*tail);
397} 400}
398 401
399/* handle a "/rmflt " request */ 402/* handle a "/rmflt " request */
400static void 403static void command_rmflt(char *tail) {
401command_rmflt (char *tail) { 404 while (*tail == ' ')
402 while( *tail == ' ') tail++; 405 tail++;
403 removefilter( tail ); 406 removefilter(tail);
404} 407}
405 408
406/* list filters */ 409/* list filters */
407static void 410static void command_lsflt(char *tail) {
408command_lsflt (char *tail) {
409 (void)tail; 411 (void)tail;
410 listfilters(); 412 listfilters();
411} 413}
412 414
413/* handle a "/me " action */ 415/* handle a "/me " action */
414static void 416static void command_action(char *tail) { doaction(tail); }
415command_action(char *tail)
416{
417 doaction(tail);
418}
419 417
420/* handle a "/reconnect" request */ 418/* handle a "/reconnect" request */
421static void 419static void command_reconnect(char *tail) {
422command_reconnect(char *tail)
423{
424 (void)tail; 420 (void)tail;
425 status = 0; 421 status = 0;
426 wantreconnect = 1; 422 wantreconnect = 1;
@@ -428,15 +424,13 @@ command_reconnect(char *tail)
428} 424}
429 425
430/* handle a "/quit " exit */ 426/* handle a "/quit " exit */
431static void 427static void command_quit(char *tail) {
432command_quit(char *tail)
433{
434 /* send users message to server */ 428 /* send users message to server */
435 snprintf (tmpstr, TMPSTRSIZE, ".x %s", tail); 429 snprintf(tmpstr, TMPSTRSIZE, ".x %s", tail);
436 vc_sendmessage (tmpstr); 430 vc_sendmessage(tmpstr);
437 431
438 /* show action in channel window */ 432 /* show action in channel window */
439 writechan (tmpstr); 433 writechan(tmpstr);
440 434
441 /* Inform vchat-client, that the closing connection 435 /* Inform vchat-client, that the closing connection
442 following is intended */ 436 following is intended */
@@ -445,42 +439,35 @@ command_quit(char *tail)
445} 439}
446 440
447/* print out version */ 441/* print out version */
448void 442void command_version(char *tail) {
449command_version(char *tail)
450{
451 (void)tail; 443 (void)tail;
452 /* output internal versions of all modules */ 444 /* output internal versions of all modules */
453 flushout(); 445 flushout();
454 writeout (vchat_cl_version); 446 writeout(vchat_cl_version);
455 writeout (vchat_ui_version); 447 writeout(vchat_ui_version);
456 writeout (vchat_io_version); 448 writeout(vchat_io_version);
457 writeout (vchat_us_version); 449 writeout(vchat_us_version);
458 writeout (vchat_cm_version); 450 writeout(vchat_cm_version);
459 writeout (vchat_tls_version); 451 writeout(vchat_tls_version);
460 writeout (vchat_tls_version_external); 452 writeout(vchat_tls_version_external);
461 showout(); 453 showout();
462} 454}
463 455
464/* start or end a query */ 456/* start or end a query */
465void 457void command_query(char *tail) {
466command_query(char *tail)
467{
468 char *msg; 458 char *msg;
469 while( *tail == ' ') tail++; 459 while (*tail == ' ')
460 tail++;
470 461
471 // Check, if a message is to be sent in first query 462 // Check, if a message is to be sent in first query
472 // Note: this is safe, since readline chops trailing spaces 463 // Note: this is safe, since readline chops trailing spaces
473 if((msg = strchr(tail, ' '))) { 464 if ((msg = strchr(tail, ' '))) {
474 privatemessagetx( tail ); 465 privatemessagetx(tail);
475 *msg = 0; 466 *msg = 0;
476 } 467 }
477 468
478 // Do the ui stuff for query 469 // Do the ui stuff for query
479 handlequery( tail ); 470 handlequery(tail);
480} 471}
481 472
482void 473void command_dict(char *tail) { ul_add_to_dict(tail); }
483command_dict(char *tail)
484{
485 ul_add_to_dict(tail);
486}
diff --git a/vchat-connection.c b/vchat-connection.c
index 5ab4dd4..01cf2c2 100644
--- a/vchat-connection.c
+++ b/vchat-connection.c
@@ -10,27 +10,27 @@
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#include <stdlib.h> 17#include <errno.h>
18#include <netdb.h>
19#include <netinet/in.h>
18#include <stddef.h> 20#include <stddef.h>
19#include <string.h>
20#include <stdio.h> 21#include <stdio.h>
21#include <unistd.h> 22#include <stdlib.h>
22#include <netdb.h> 23#include <string.h>
23#include <errno.h>
24#include <sys/types.h>
25#include <sys/socket.h> 24#include <sys/socket.h>
26#include <netinet/in.h> 25#include <sys/types.h>
26#include <unistd.h>
27 27
28/* For tilde_expand */ 28/* For tilde_expand */
29#include <readline/readline.h> 29#include <readline/readline.h>
30 30
31#include "vchat.h"
32#include "vchat-connection.h" 31#include "vchat-connection.h"
33#include "vchat-tls.h" 32#include "vchat-tls.h"
33#include "vchat.h"
34 34
35static int serverfd = -1; 35static int serverfd = -1;
36unsigned int want_tcp_keepalive = 0; 36unsigned int want_tcp_keepalive = 0;
@@ -40,49 +40,49 @@ unsigned int want_tcp_keepalive = 0;
40extern int status; 40extern int status;
41 41
42/* Generic tcp connector, blocking */ 42/* Generic tcp connector, blocking */
43static int connect_tcp_socket( const char *server, const char *port ) { 43static int connect_tcp_socket(const char *server, const char *port) {
44 struct addrinfo hints, *res, *res0; 44 struct addrinfo hints, *res, *res0;
45 int s, error; 45 int s, error;
46 46
47 memset(&hints, 0, sizeof(hints)); 47 memset(&hints, 0, sizeof(hints));
48 hints.ai_family = PF_UNSPEC; 48 hints.ai_family = PF_UNSPEC;
49 hints.ai_socktype = SOCK_STREAM; 49 hints.ai_socktype = SOCK_STREAM;
50 error = getaddrinfo( server, port, &hints, &res0 ); 50 error = getaddrinfo(server, port, &hints, &res0);
51 if (error) return -1; 51 if (error)
52 return -1;
52 s = -1; 53 s = -1;
53 for (res = res0; res; res = res->ai_next) { 54 for (res = res0; res; res = res->ai_next) {
54 s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); 55 s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
55 if (s < 0) continue; 56 if (s < 0)
57 continue;
56 if (connect(s, res->ai_addr, res->ai_addrlen) < 0) { 58 if (connect(s, res->ai_addr, res->ai_addrlen) < 0) {
57 close(s); 59 close(s);
58 s = -1; 60 s = -1;
59 continue; 61 continue;
60 } 62 }
61 break; /* okay we got one */ 63 break; /* okay we got one */
62 } 64 }
63 freeaddrinfo(res0); 65 freeaddrinfo(res0);
64 66
65 if (want_tcp_keepalive) { 67 if (want_tcp_keepalive) {
66 int one=1; 68 int one = 1;
67 setsockopt(s,SOL_SOCKET,SO_KEEPALIVE,&one,sizeof(one)); 69 setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof(one));
68 } 70 }
69 return s; 71 return s;
70} 72}
71 73
72/* Return a tilde expanded path in a malloced buffer or NULL */ 74/* Return a tilde expanded path in a malloced buffer or NULL */
73static char *get_tilde_expanded(confopt opt) { 75static char *get_tilde_expanded(confopt opt) {
74 char *str = getstroption (opt); 76 char *str = getstroption(opt);
75 if (!str) 77 if (!str)
76 return str; 78 return str;
77 if (str[0] == '~') 79 if (str[0] == '~')
78 return tilde_expand (str); 80 return tilde_expand(str);
79 return strdup(str); 81 return strdup(str);
80} 82}
81 83
82/* connects to server */ 84/* connects to server */
83int 85int vc_connect(const char *server, const char *port) {
84vc_connect (const char *server, const char *port)
85{
86 /* vchat connection x509 store */ 86 /* vchat connection x509 store */
87 vc_x509store_t vc_store; 87 vc_x509store_t vc_store;
88 88
@@ -90,11 +90,11 @@ vc_connect (const char *server, const char *port)
90 char *certfile, *cafile; 90 char *certfile, *cafile;
91 91
92 /* Connect to the server */ 92 /* Connect to the server */
93 serverfd = connect_tcp_socket( server, port ); 93 serverfd = connect_tcp_socket(server, port);
94 if( serverfd < 0 ) { 94 if (serverfd < 0) {
95 /* inform user */ 95 /* inform user */
96 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_CANTCONNECT), server, port ); 96 snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_CANTCONNECT), server, port);
97 writechan (tmpstr); 97 writechan(tmpstr);
98 return -1; 98 return -1;
99 } 99 }
100 100
@@ -105,11 +105,11 @@ vc_connect (const char *server, const char *port)
105 vc_init_x509store(&vc_store); 105 vc_init_x509store(&vc_store);
106 106
107 /* get name of certificate file */ 107 /* get name of certificate file */
108 certfile = get_tilde_expanded (CF_CERTFILE); 108 certfile = get_tilde_expanded(CF_CERTFILE);
109 /* do we have a certificate file? */ 109 /* do we have a certificate file? */
110 if (certfile) { 110 if (certfile) {
111 /* get name of key file */ 111 /* get name of key file */
112 char *keyfile = get_tilde_expanded (CF_KEYFILE); 112 char *keyfile = get_tilde_expanded(CF_KEYFILE);
113 113
114 vc_x509store_setcertfile(&vc_store, certfile); 114 vc_x509store_setcertfile(&vc_store, certfile);
115 vc_x509store_set_pkeycb(&vc_store, (vc_askpass_cb_t)passprompt); 115 vc_x509store_set_pkeycb(&vc_store, (vc_askpass_cb_t)passprompt);
@@ -122,13 +122,13 @@ vc_connect (const char *server, const char *port)
122 } 122 }
123 123
124 /* get name of ca file */ 124 /* get name of ca file */
125 cafile = get_tilde_expanded (CF_CAFILE); 125 cafile = get_tilde_expanded(CF_CAFILE);
126 if (cafile && !access(cafile, F_OK)) 126 if (cafile && !access(cafile, F_OK))
127 vc_x509store_setcafile(&vc_store, cafile); 127 vc_x509store_setcafile(&vc_store, cafile);
128 free(cafile); 128 free(cafile);
129 129
130 /* upgrade our plain BIO to ssl */ 130 /* upgrade our plain BIO to ssl */
131 int result = vc_tls_connect( serverfd, &vc_store ); 131 int result = vc_tls_connect(serverfd, &vc_store);
132 vc_cleanup_x509store(&vc_store); 132 vc_cleanup_x509store(&vc_store);
133 133
134 if (result) { 134 if (result) {
@@ -136,17 +136,17 @@ vc_connect (const char *server, const char *port)
136 serverfd = -1; 136 serverfd = -1;
137 errno = EIO; 137 errno = EIO;
138 vc_tls_cleanup(); 138 vc_tls_cleanup();
139 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_CANTCONNECT), server, port ); 139 snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_CANTCONNECT), server, port);
140 writechan (tmpstr); 140 writechan(tmpstr);
141 return -1; 141 return -1;
142 } 142 }
143 143
144 /* inform user */ 144 /* inform user */
145 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_CONNECTED), server, port); 145 snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_CONNECTED), server, port);
146 writechan (tmpstr); 146 writechan(tmpstr);
147 147
148#ifdef DEBUG 148#ifdef DEBUG
149 dumpfile = fopen( "dumpfile", "a"); 149 dumpfile = fopen("dumpfile", "a");
150#endif 150#endif
151 151
152 /* if we didn't fail until now, we've got a connection. */ 152 /* if we didn't fail until now, we've got a connection. */
@@ -154,25 +154,24 @@ vc_connect (const char *server, const char *port)
154} 154}
155 155
156/* Poll for activity on the socket or stdin */ 156/* Poll for activity on the socket or stdin */
157int vc_poll (int timeout_seconds) { 157int vc_poll(int timeout_seconds) {
158 fd_set readfds; 158 fd_set readfds;
159 FD_ZERO (&readfds); 159 FD_ZERO(&readfds);
160 FD_SET (0, &readfds); 160 FD_SET(0, &readfds);
161 if (serverfd != -1) 161 if (serverfd != -1)
162 FD_SET (serverfd, &readfds); 162 FD_SET(serverfd, &readfds);
163 struct timeval tv = { timeout_seconds, 0}; 163 struct timeval tv = {timeout_seconds, 0};
164 int result = select (serverfd + 2, &readfds, NULL, NULL, &tv); 164 int result = select(serverfd + 2, &readfds, NULL, NULL, &tv);
165 if (result <= 0) 165 if (result <= 0)
166 return result; 166 return result;
167 result = FD_ISSET (0, &readfds) ? 1 : 0; 167 result = FD_ISSET(0, &readfds) ? 1 : 0;
168 if (serverfd != -1) 168 if (serverfd != -1)
169 result += FD_ISSET (serverfd, &readfds) ? 2 : 0; 169 result += FD_ISSET(serverfd, &readfds) ? 2 : 0;
170 return result; 170 return result;
171} 171}
172 172
173/* disconnect from server */ 173/* disconnect from server */
174void 174void vc_disconnect() {
175vc_disconnect () {
176 if (serverfd > 0) { 175 if (serverfd > 0) {
177 close(serverfd); 176 close(serverfd);
178 serverfd = -1; 177 serverfd = -1;
@@ -183,21 +182,19 @@ vc_disconnect () {
183 182
184#define STAGINGSIZE 16384 183#define STAGINGSIZE 16384
185static char _staging[STAGINGSIZE]; 184static char _staging[STAGINGSIZE];
186void 185void vc_sendmessage(const char *msg) {
187vc_sendmessage (const char *msg)
188{
189 size_t sent, len = snprintf(_staging, sizeof(_staging), "%s\r\n", msg); 186 size_t sent, len = snprintf(_staging, sizeof(_staging), "%s\r\n", msg);
190#ifdef DEBUG 187#ifdef DEBUG
191 /* debugging? log network output! */ 188 /* debugging? log network output! */
192 fprintf (dumpfile, ">| (%zd) %s\n", len - 2, msg); 189 fprintf(dumpfile, ">| (%zd) %s\n", len - 2, msg);
193#endif 190#endif
194 191
195 if (getintoption(CF_USESSL)) 192 if (getintoption(CF_USESSL))
196 sent = vc_tls_sendmessage (_staging, len); 193 sent = vc_tls_sendmessage(_staging, len);
197 else 194 else
198 sent = write (serverfd, _staging, len); 195 sent = write(serverfd, _staging, len);
199 if (sent != len) 196 if (sent != len)
200 writecf (FS_ERR,"Message sending fuzzy."); 197 writecf(FS_ERR, "Message sending fuzzy.");
201} 198}
202 199
203/* offset in buffer (for linebreaks at packet borders) */ 200/* offset in buffer (for linebreaks at packet borders) */
@@ -206,9 +203,7 @@ static char _buf[BUFSIZE];
206static size_t _buf_fill; 203static size_t _buf_fill;
207 204
208/* get data from servers connection */ 205/* get data from servers connection */
209void 206void vc_receive(void) {
210vc_receive (void)
211{
212 char *endmsg; 207 char *endmsg;
213 size_t freebytes = BUFSIZE - _buf_fill; 208 size_t freebytes = BUFSIZE - _buf_fill;
214 ssize_t bytes; 209 ssize_t bytes;
@@ -218,26 +213,27 @@ vc_receive (void)
218 else 213 else
219 bytes = vc_tls_receivemessage(_buf + _buf_fill, freebytes); 214 bytes = vc_tls_receivemessage(_buf + _buf_fill, freebytes);
220 215
221 /* Our tls functions may require retries with handshakes etc, this is signalled by -2 */ 216 /* Our tls functions may require retries with handshakes etc, this is
217 * signalled by -2 */
222 if (bytes == -2) 218 if (bytes == -2)
223 return; 219 return;
224 220
225 /* Error on the socket read? raise error message, bail out */ 221 /* Error on the socket read? raise error message, bail out */
226 if (bytes == -1) { 222 if (bytes == -1) {
227 snprintf (tmpstr, TMPSTRSIZE, "Receive fails, %s.", strerror(errno)); 223 snprintf(tmpstr, TMPSTRSIZE, "Receive fails, %s.", strerror(errno));
228 snprintf (errstr, ERRSTRSIZE, "Receive fails, %s.\n", strerror(errno)); 224 snprintf(errstr, ERRSTRSIZE, "Receive fails, %s.\n", strerror(errno));
229 writecf (FS_ERR,tmpstr); 225 writecf(FS_ERR, tmpstr);
230 status = 0; 226 status = 0;
231 return; 227 return;
232 } 228 }
233 229
234 /* end of file from server? */ 230 /* end of file from server? */
235 if (bytes == 0) { 231 if (bytes == 0) {
236 /* inform user, bail out */ 232 /* inform user, bail out */
237 writecf (FS_SERV, "* EOF from server."); 233 writecf(FS_SERV, "* EOF from server.");
238 snprintf (errstr, ERRSTRSIZE, "* EOF from server.\n"); 234 snprintf(errstr, ERRSTRSIZE, "* EOF from server.\n");
239 status = 0; 235 status = 0;
240 return; 236 return;
241 } 237 }
242 238
243 _buf_fill += bytes; 239 _buf_fill += bytes;
@@ -249,13 +245,14 @@ vc_receive (void)
249 endmsg[0] = 0; 245 endmsg[0] = 0;
250 if (endmsg[-1] == '\r') 246 if (endmsg[-1] == '\r')
251 endmsg[-1] = 0; 247 endmsg[-1] = 0;
252 /* If terminating and chomping left us with a message, give it to line handler */ 248 /* If terminating and chomping left us with a message, give it to line
249 * handler */
253 if (_buf[0]) { 250 if (_buf[0]) {
254#ifdef DEBUG 251#ifdef DEBUG
255 /* debugging? log network input! */ 252 /* debugging? log network input! */
256 fprintf (stderr, "<| %s\n", _buf); 253 fprintf(stderr, "<| %s\n", _buf);
257#endif 254#endif
258 protocol_parsemsg (_buf); 255 protocol_parsemsg(_buf);
259 } 256 }
260 } 257 }
261 _buf_fill -= 1 + endmsg - _buf; 258 _buf_fill -= 1 + endmsg - _buf;
diff --git a/vchat-connection.h b/vchat-connection.h
index a56dc29..9b3ccaa 100644
--- a/vchat-connection.h
+++ b/vchat-connection.h
@@ -2,8 +2,8 @@
2 2
3#include <stdint.h> 3#include <stdint.h>
4 4
5int vc_connect(const char *host, const char *port); 5int vc_connect(const char *host, const char *port);
6void vc_sendmessage(const char *message); 6void vc_sendmessage(const char *message);
7void vc_receive(); 7void vc_receive();
8int vc_poll(); 8int vc_poll();
9void vc_disconnect(); 9void vc_disconnect();
diff --git a/vchat-protocol.c b/vchat-protocol.c
index 2b98779..ac65639 100755
--- a/vchat-protocol.c
+++ b/vchat-protocol.c
@@ -10,49 +10,51 @@
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> 18#include <errno.h>
19#include <langinfo.h>
20#include <locale.h>
19#include <stdio.h> 21#include <stdio.h>
22#include <stdlib.h>
20#include <string.h> 23#include <string.h>
21#include <errno.h> 24
22#include <readline/readline.h> 25#include <readline/readline.h>
23#include <locale.h>
24#include <langinfo.h>
25 26
26#ifdef DEBUG 27#ifdef DEBUG
27FILE * dumpfile; 28FILE *dumpfile;
28#endif 29#endif
29 30
30/* local includes */ 31/* local includes */
31#include "vchat.h"
32#include "vchat-user.h"
33#include "vchat-connection.h" 32#include "vchat-connection.h"
33#include "vchat-user.h"
34#include "vchat.h"
34 35
35/* version of this module */ 36/* version of this module */
36const char *vchat_io_version = "vchat-protocol.c $Id$"; 37const char *vchat_io_version =
38 "vchat-protocol.c $Id$";
37 39
38/* declaration of local helper functions */ 40/* declaration of local helper functions */
39static void usersignon (char *); 41static void usersignon(char *);
40static void usersignoff (char *); 42static void usersignoff(char *);
41static void usernickchange (char *); 43static void usernickchange(char *);
42static void userjoin (char *); 44static void userjoin(char *);
43static void userleave (char *); 45static void userleave(char *);
44static void receivenicks (char *message); 46static void receivenicks(char *message);
45static void justloggedin (char *message); 47static void justloggedin(char *message);
46static void nickerr (char *message); 48static void nickerr(char *message);
47static void login (char *message); 49static void login(char *message);
48static void anonlogin (char *message); 50static void anonlogin(char *message);
49static void topicinfo (char *message); 51static void topicinfo(char *message);
50static void pubaction (char *message); 52static void pubaction(char *message);
51static void pubthoughts (char *message); 53static void pubthoughts(char *message);
52static void serverlogin (char *message); 54static void serverlogin(char *message);
53static void idleprompt (char *message); 55static void idleprompt(char *message);
54static void topicchange (char *message); 56static void topicchange(char *message);
55static void pmnotsent (char *message); 57static void pmnotsent(char *message);
56 58
57/* declaration of server message array */ 59/* declaration of server message array */
58#include "vchat-messages.h" 60#include "vchat-messages.h"
@@ -61,61 +63,53 @@ char *encoding;
61 63
62/* handle a pm not sent error 64/* handle a pm not sent error
63 * format: 412 %s */ 65 * format: 412 %s */
64static void 66static void pmnotsent(char *message) {
65pmnotsent (char *message) 67 while (*message && *message != ' ')
66{ 68 message++;
67 while(*message && *message!=' ') message++; 69 snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_ERR), message + 1);
68 snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_ERR),message+1); 70 writepriv(tmpstr, 0);
69 writepriv( tmpstr, 0);
70
71} 71}
72 72
73/* parse and handle an action string 73/* parse and handle an action string
74 * format: 118 %s %s 74 * format: 118 %s %s
75 * vars: %s nick 75 * vars: %s nick
76 * %s action */ 76 * %s action */
77static void 77static void pubaction(char *message) {
78pubaction (char *message)
79{
80 char *nick = NULL, *action = NULL; 78 char *nick = NULL, *action = NULL;
81 nick = strchr (message, ' '); 79 nick = strchr(message, ' ');
82 nick[0] = '\0'; 80 nick[0] = '\0';
83 nick++; 81 nick++;
84 82
85 action = strchr (nick, ' '); 83 action = strchr(nick, ' ');
86 action[0] = '\0'; 84 action[0] = '\0';
87 action++; 85 action++;
88 86
89 ul_public_action(nick); 87 ul_public_action(nick);
90 snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_PUBACTION),nick,action); 88 snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_PUBACTION), nick, action);
91 writechan (tmpstr); 89 writechan(tmpstr);
92} 90}
93 91
94/* parse and handle an thought string 92/* parse and handle an thought string
95 * format: 124 %s %s 93 * format: 124 %s %s
96 * vars: %s nick 94 * vars: %s nick
97 * %s thought */ 95 * %s thought */
98static void 96static void pubthoughts(char *message) {
99pubthoughts (char *message)
100{
101 char *nick = NULL, *thoughts = NULL; 97 char *nick = NULL, *thoughts = NULL;
102 nick = strchr (message, ' '); 98 nick = strchr(message, ' ');
103 nick[0] = '\0'; 99 nick[0] = '\0';
104 nick++; 100 nick++;
105 101
106 thoughts = strchr (nick, ' '); 102 thoughts = strchr(nick, ' ');
107 thoughts[0] = '\0'; 103 thoughts[0] = '\0';
108 thoughts++; 104 thoughts++;
109 105
110 ul_public_action(nick); 106 ul_public_action(nick);
111 snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_PUBTHOUGHT),nick,thoughts); 107 snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_PUBTHOUGHT), nick, thoughts);
112 writechan (tmpstr); 108 writechan(tmpstr);
113} 109}
114 110
115/* parse and handle server logon */ 111/* parse and handle server logon */
116static void 112static void serverlogin(char *message) {
117serverlogin (char *message)
118{
119 (void)message; 113 (void)message;
120 int utf8 = !strcmp(nl_langinfo(CODESET), "UTF-8"); 114 int utf8 = !strcmp(nl_langinfo(CODESET), "UTF-8");
121 if (utf8) 115 if (utf8)
@@ -125,274 +119,257 @@ serverlogin (char *message)
125/* parse and handle an idle message 119/* parse and handle an idle message
126 * format: 305 120 * format: 305
127 * vars: %s message */ 121 * vars: %s message */
128static void 122static void idleprompt(char *message) {
129idleprompt (char *message)
130{
131 char *msg = NULL; 123 char *msg = NULL;
132 msg = strchr (message, ' '); 124 msg = strchr(message, ' ');
133 msg[0] = '\0'; 125 msg[0] = '\0';
134 msg++; 126 msg++;
135 127
136 snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_IDLE),msg); 128 snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_IDLE), msg);
137 writechan (tmpstr); 129 writechan(tmpstr);
138} 130}
139 131
140/* parse and handle a topic information string 132/* parse and handle a topic information string
141 * format: 115 %d %s 133 * format: 115 %d %s
142 * vars: %d chan - channel number 134 * vars: %d chan - channel number
143 * %s topic - topic */ 135 * %s topic - topic */
144static void 136static void topicinfo(char *message) {
145topicinfo (char *message)
146{
147 char *channel = NULL, *topic = NULL; 137 char *channel = NULL, *topic = NULL;
148 int tmpchan = 0, ownchan = own_channel_get(); 138 int tmpchan = 0, ownchan = own_channel_get();
149 139
150 /* search start of channel number */ 140 /* search start of channel number */
151 channel = strchr (message, ' '); 141 channel = strchr(message, ' ');
152 channel++; 142 channel++;
153 143
154 /* search start of topic and terminate channel number */ 144 /* search start of topic and terminate channel number */
155 topic = strchr (channel, ' '); 145 topic = strchr(channel, ' ');
156 topic[0] = '\0'; 146 topic[0] = '\0';
157 topic++; 147 topic++;
158 148
159 /* convert channel number to integer */ 149 /* convert channel number to integer */
160 tmpchan = atoi (channel); 150 tmpchan = atoi(channel);
161 151
162 if (tmpchan == ownchan ) { 152 if (tmpchan == ownchan) {
163 /* show change in topic window */ 153 /* show change in topic window */
164 if (strlen(topic)) 154 if (strlen(topic))
165 snprintf (topicstr, TOPICSTRSIZE, getformatstr(FS_TOPICW), ownchan, topic); 155 snprintf(topicstr, TOPICSTRSIZE, getformatstr(FS_TOPICW), ownchan, topic);
166 else 156 else
167 snprintf (topicstr, TOPICSTRSIZE, getformatstr(FS_NOTOPICW), ownchan ); 157 snprintf(topicstr, TOPICSTRSIZE, getformatstr(FS_NOTOPICW), ownchan);
168 topicline(NULL); 158 topicline(NULL);
169 } 159 }
170 160
171 /* announce change in channel window */ 161 /* announce change in channel window */
172 if (strlen(topic)) 162 if (strlen(topic))
173 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_TOPIC), tmpchan, topic); 163 snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_TOPIC), tmpchan, topic);
174 else 164 else
175 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_NOTOPIC), tmpchan); 165 snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_NOTOPIC), tmpchan);
176 writechan (tmpstr); 166 writechan(tmpstr);
177} 167}
178 168
179/* parse and handle a topic change string 169/* parse and handle a topic change string
180 * format: 114 %s changes topic to '%s' 170 * format: 114 %s changes topic to '%s'
181 * vars: %s nick 171 * vars: %s nick
182 * %s topic */ 172 * %s topic */
183static void 173static void topicchange(char *message) {
184topicchange (char *message)
185{
186 char *nick = NULL, *topic = NULL; 174 char *nick = NULL, *topic = NULL;
187 int len, ownchan = own_channel_get(); 175 int len, ownchan = own_channel_get();
188 176
189 /* search start of nickname */ 177 /* search start of nickname */
190 nick = strchr (message, ' '); 178 nick = strchr(message, ' ');
191 nick++; 179 nick++;
192 180
193 /* search start of message before topic, terminate nick */ 181 /* search start of message before topic, terminate nick */
194 topic = strchr (nick, ' '); 182 topic = strchr(nick, ' ');
195 topic[0] = '\0'; 183 topic[0] = '\0';
196 topic++; 184 topic++;
197 185
198 /* search start of real topic and terminate channel number */ 186 /* search start of real topic and terminate channel number */
199 topic = strchr (topic, '\''); 187 topic = strchr(topic, '\'');
200 topic[0] = '\0'; 188 topic[0] = '\0';
201 topic++; 189 topic++;
202 190
203 /* remove ending '\'', if there */ 191 /* remove ending '\'', if there */
204 len = strlen (topic); 192 len = strlen(topic);
205 if (topic[len-1] == '\'') 193 if (topic[len - 1] == '\'')
206 topic[len-1] = '\0'; 194 topic[len - 1] = '\0';
207 195
208 ul_public_action(nick); 196 ul_public_action(nick);
209 /* show change in topic window */ 197 /* show change in topic window */
210 snprintf (topicstr, TOPICSTRSIZE, getformatstr(FS_TOPICW), ownchan, topic); 198 snprintf(topicstr, TOPICSTRSIZE, getformatstr(FS_TOPICW), ownchan, topic);
211 topicline(NULL); 199 topicline(NULL);
212 200
213 /* announce change in channel window */ 201 /* announce change in channel window */
214 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_CHGTOPIC), nick, topic); 202 snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_CHGTOPIC), nick, topic);
215 writechan (tmpstr); 203 writechan(tmpstr);
216} 204}
217 205
218/* parse and handle a login message 206/* parse and handle a login message
219 * format: 212 %s %s 207 * format: 212 %s %s
220 * vars: %s str1 - nick used to login 208 * vars: %s str1 - nick used to login
221 * %s str2 - servers message */ 209 * %s str2 - servers message */
222static void 210static void justloggedin(char *message) {
223justloggedin (char *message)
224{
225 char *str1 = NULL, *str2 = NULL; 211 char *str1 = NULL, *str2 = NULL;
226 /* search start of nickname */ 212 /* search start of nickname */
227 str1 = strchr (message, ' '); 213 str1 = strchr(message, ' ');
228 str1++; 214 str1++;
229 215
230 /* search start of message, terminate nick */ 216 /* search start of message, terminate nick */
231 str2 = strchr (str1, ' '); 217 str2 = strchr(str1, ' ');
232 str2[0] = '\0'; 218 str2[0] = '\0';
233 str2++; 219 str2++;
234 220
235 /* if we have a new nick, store it */ 221 /* if we have a new nick, store it */
236 own_nick_set( str1 ); 222 own_nick_set(str1);
237 223
238 /* show change in console window */ 224 /* show change in console window */
239 snprintf (consolestr, CONSOLESTRSIZE, getformatstr(FS_CONSOLE), str1, getstroption (CF_SERVERHOST), getstroption (CF_SERVERPORT)); 225 snprintf(consolestr, CONSOLESTRSIZE, getformatstr(FS_CONSOLE), str1,
240 consoleline (NULL); 226 getstroption(CF_SERVERHOST), getstroption(CF_SERVERPORT));
227 consoleline(NULL);
241 228
242 /* announce login as servermessage */ 229 /* announce login as servermessage */
243 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_SIGNON), str1, str2); 230 snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_SIGNON), str1, str2);
244 writechan (tmpstr); 231 writechan(tmpstr);
245 232
246 /* we're not logged in, change status and request nicks */ 233 /* we're not logged in, change status and request nicks */
247 if (!loggedin) 234 if (!loggedin) {
248 { 235 loadcfg(getstroption(CF_LOGINSCRIPT), 0, handleline);
249 loadcfg(getstroption(CF_LOGINSCRIPT),0,handleline); 236 handleline(".S");
250 handleline(".S"); 237 loggedin = 1;
251 loggedin = 1; 238 }
252 }
253} 239}
254 240
255/* this user joins a different channel */ 241/* this user joins a different channel */
256void 242void ownjoin(int channel) {
257ownjoin (int channel)
258{
259 vc_sendmessage(".t"); 243 vc_sendmessage(".t");
260 snprintf(tmpstr, TMPSTRSIZE, ".S %d",channel); 244 snprintf(tmpstr, TMPSTRSIZE, ".S %d", channel);
261 vc_sendmessage(tmpstr); 245 vc_sendmessage(tmpstr);
262} 246}
263 247
264/* this user changes his nick */ 248/* this user changes his nick */
265void 249void ownnickchange(const char *newnick) { (void)newnick; }
266ownnickchange (const char *newnick)
267{
268 (void)newnick;
269}
270 250
271/* parse and handle a nick error message 251/* parse and handle a nick error message
272 * format: 401 %s 252 * format: 401 %s
273 * 403 %s 253 * 403 %s
274 * 415 %s 254 * 415 %s
275 * vars: %s - server message */ 255 * vars: %s - server message */
276static void 256static void nickerr(char *message) {
277nickerr (char *message)
278{
279 char *helpkiller = NULL; 257 char *helpkiller = NULL;
280 /* mutate message for output */ 258 /* mutate message for output */
281 message[2] = '!'; 259 message[2] = '!';
282 /* help information found? remove it. */ 260 /* help information found? remove it. */
283 if ((helpkiller = strstr (message, " Type .h for help"))) 261 if ((helpkiller = strstr(message, " Type .h for help")))
284 helpkiller[0] = '\0'; 262 helpkiller[0] = '\0';
285 /* nickchange not done? eliminate message */ 263 /* nickchange not done? eliminate message */
286 if (loggedin && (helpkiller = strstr (message, " - Nick not changed"))) 264 if (loggedin && (helpkiller = strstr(message, " - Nick not changed")))
287 helpkiller[0] = '\0'; 265 helpkiller[0] = '\0';
288 /* show errormessage */ 266 /* show errormessage */
289 writecf (FS_ERR,&message[2]); 267 writecf(FS_ERR, &message[2]);
290 268
291 /* not logged in? insist on getting a new nick */ 269 /* not logged in? insist on getting a new nick */
292 if (!loggedin) 270 if (!loggedin) {
293 { 271 /* free bogus nick */
294 /* free bogus nick */ 272 own_nick_set(NULL);
295 own_nick_set(NULL);
296 273
297 /* get new nick via vchat-ui.c */ 274 /* get new nick via vchat-ui.c */
298 nickprompt (); 275 nickprompt();
299 276
300 /* form login message and send it to server */ 277 /* form login message and send it to server */
301 snprintf (tmpstr, TMPSTRSIZE, ".l %s %s %d", own_nick_get(), getstroption (CF_FROM), getintoption (CF_CHANNEL)); 278 snprintf(tmpstr, TMPSTRSIZE, ".l %s %s %d", own_nick_get(),
302 vc_sendmessage (tmpstr); 279 getstroption(CF_FROM), getintoption(CF_CHANNEL));
303 } 280 vc_sendmessage(tmpstr);
281 }
304} 282}
305 283
306/* parse and handle a registered nick information 284/* parse and handle a registered nick information
307 * format: 120 %s %s 285 * format: 120 %s %s
308 * vars: %s - this users registered nick 286 * vars: %s - this users registered nick
309 * %s msg - server message */ 287 * %s msg - server message */
310static void 288static void login(char *message) {
311login (char *message) {
312 char *msg = NULL; 289 char *msg = NULL;
313 290
314 /* mutate message for output */ 291 /* mutate message for output */
315 message[2] = '*'; 292 message[2] = '*';
316 /* show message to user */ 293 /* show message to user */
317 writecf (FS_SERV,&message[2]); 294 writecf(FS_SERV, &message[2]);
318 295
319 /* we don't know our nick? */ 296 /* we don't know our nick? */
320 if (!own_nick_get() ) { 297 if (!own_nick_get()) {
321 /* find message after nick */ 298 /* find message after nick */
322 msg = strchr (&message[4], ' '); 299 msg = strchr(&message[4], ' ');
323 if (msg) { 300 if (msg) {
324 /* terminate string before message and copy nick */ 301 /* terminate string before message and copy nick */
325 msg[0] = '\0'; 302 msg[0] = '\0';
326 own_nick_set(&message[4]); 303 own_nick_set(&message[4]);
327 } else { 304 } else {
328 /* no string in servers message (huh?), ask user for nick */ 305 /* no string in servers message (huh?), ask user for nick */
329 nickprompt (); 306 nickprompt();
330 } 307 }
331 } 308 }
332 309
333 /* form login message and send it to server */ 310 /* form login message and send it to server */
334 snprintf (tmpstr, TMPSTRSIZE, ".l %s %s %d", own_nick_get(), getstroption (CF_FROM), getintoption (CF_CHANNEL)); 311 snprintf(tmpstr, TMPSTRSIZE, ".l %s %s %d", own_nick_get(),
335 vc_sendmessage (tmpstr); 312 getstroption(CF_FROM), getintoption(CF_CHANNEL));
313 vc_sendmessage(tmpstr);
336} 314}
337 315
338/* parse and handle anon login request 316/* parse and handle anon login request
339 * format: 121 %s 317 * format: 121 %s
340 * vars: %s - server message */ 318 * vars: %s - server message */
341static void 319static void anonlogin(char *message) {
342anonlogin (char *message)
343{
344 /* mutate message for output */ 320 /* mutate message for output */
345 message[2] = '*'; 321 message[2] = '*';
346 /* show message to user */ 322 /* show message to user */
347 writecf (FS_SERV,&message[2]); 323 writecf(FS_SERV, &message[2]);
348 324
349 /* we don't know our nick? ask for it! */ 325 /* we don't know our nick? ask for it! */
350 if (!own_nick_get()) 326 if (!own_nick_get())
351 nickprompt (); 327 nickprompt();
352 328
353 /* form login message and send it to server */ 329 /* form login message and send it to server */
354 snprintf (tmpstr, TMPSTRSIZE, ".l %s %s %d", own_nick_get(), getstroption (CF_FROM), getintoption (CF_CHANNEL)); 330 snprintf(tmpstr, TMPSTRSIZE, ".l %s %s %d", own_nick_get(),
355 vc_sendmessage (tmpstr); 331 getstroption(CF_FROM), getintoption(CF_CHANNEL));
332 vc_sendmessage(tmpstr);
356} 333}
357 334
358/* parse and handle list of nicks (from '.S') 335/* parse and handle list of nicks (from '.S')
359 * format: 119 %s .. 336 * format: 119 %s ..
360 * vars: %s nick - a users nick */ 337 * vars: %s nick - a users nick */
361static void 338static void receivenicks(char *message) {
362receivenicks (char *message) {
363 char *str1 = NULL, *str2 = NULL; 339 char *str1 = NULL, *str2 = NULL;
364 int chanflag = -1; 340 int chanflag = -1;
365 341
366 /* show message to user */ 342 /* show message to user */
367 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_USONLINE), &message[4]); 343 snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_USONLINE), &message[4]);
368 writechan (tmpstr); 344 writechan(tmpstr);
369 345
370 /* search for channelnumber */ 346 /* search for channelnumber */
371 if( !(str1 = strchr (message, ' ') ) ) return; 347 if (!(str1 = strchr(message, ' ')))
348 return;
372 str1++; 349 str1++;
373 350
374 if (str1[0] == '*') { 351 if (str1[0] == '*') {
375 ul_rebuild_list(); 352 ul_rebuild_list();
376 str1++; 353 str1++;
377 } else { 354 } else {
378 int mychan; 355 int mychan;
379 str2 = str1; 356 str2 = str1;
380 str1 = strchr(str2,' '); 357 str1 = strchr(str2, ' ');
381 str1[0] = '\0'; 358 str1[0] = '\0';
382 mychan = atoi(str2); 359 mychan = atoi(str2);
383 if( mychan != own_channel_get() ) 360 if (mychan != own_channel_get())
384 return; 361 return;
385 362
386 /* Kick all users from the IN_MY_CHANNEL list */ 363 /* Kick all users from the IN_MY_CHANNEL list */
387 own_channel_set( own_channel_get() ); 364 own_channel_set(own_channel_get());
388 chanflag = 1; 365 chanflag = 1;
389 } 366 }
390 str1++; 367 str1++;
391 368
392 /* while user .. */ 369 /* while user .. */
393 while (str1) { 370 while (str1) {
394 /* search next user */ 371 /* search next user */
395 str2 = strchr (str1, ' '); 372 str2 = strchr(str1, ' ');
396 /* there is another user? terminate this one */ 373 /* there is another user? terminate this one */
397 if (str2) { 374 if (str2) {
398 str2[0] = '\0'; 375 str2[0] = '\0';
@@ -412,52 +389,48 @@ receivenicks (char *message) {
412 * format: 211 %s %s 389 * format: 211 %s %s
413 * vars: %s nick - who logged on 390 * vars: %s nick - who logged on
414 * %s msg - servers message */ 391 * %s msg - servers message */
415static void 392static void usersignon(char *message) {
416usersignon (char *message)
417{
418 char *nick = NULL, *msg = NULL; 393 char *nick = NULL, *msg = NULL;
419 /* search start of nickname */ 394 /* search start of nickname */
420 nick = strchr (message, ' '); 395 nick = strchr(message, ' ');
421 nick++; 396 nick++;
422 397
423 /* search start of message, terminate nick */ 398 /* search start of message, terminate nick */
424 msg = strchr (nick, ' '); 399 msg = strchr(nick, ' ');
425 msg[0] = '\0'; 400 msg[0] = '\0';
426 msg++; 401 msg++;
427 402
428 /* add this user via vchat-user.c */ 403 /* add this user via vchat-user.c */
429 ul_add (nick, 0); 404 ul_add(nick, 0);
430 405
431 /* show message to user */ 406 /* show message to user */
432 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_SIGNON), nick, msg); 407 snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_SIGNON), nick, msg);
433 writechan (tmpstr); 408 writechan(tmpstr);
434} 409}
435 410
436/* parse and handle a logoff message 411/* parse and handle a logoff message
437 * format: 221 %s %s 412 * format: 221 %s %s
438 * vars: %s nick - who logged off 413 * vars: %s nick - who logged off
439 * %s msg - servers message */ 414 * %s msg - servers message */
440static void 415static void usersignoff(char *message) {
441usersignoff (char *message)
442{
443 char *nick = NULL, *msg = NULL; 416 char *nick = NULL, *msg = NULL;
444 /* search start of nickname */ 417 /* search start of nickname */
445 nick = strchr (message, ' '); 418 nick = strchr(message, ' ');
446 nick++; 419 nick++;
447 420
448 /* search start of message, terminate nick */ 421 /* search start of message, terminate nick */
449 msg = strchr (nick, ' '); 422 msg = strchr(nick, ' ');
450 if( msg ) { 423 if (msg) {
451 msg[0] = '\0'; 424 msg[0] = '\0';
452 msg++; 425 msg++;
453 } 426 }
454 427
455 /* delete this user via vchat-user.c */ 428 /* delete this user via vchat-user.c */
456 ul_del (nick); 429 ul_del(nick);
457 430
458 /* show message to user */ 431 /* show message to user */
459 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_SIGNOFF), nick, msg); 432 snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_SIGNOFF), nick, msg);
460 writechan (tmpstr); 433 writechan(tmpstr);
461} 434}
462 435
463/* parse and handle a join message 436/* parse and handle a join message
@@ -465,40 +438,38 @@ usersignoff (char *message)
465 * vars: %s nick - who joined 438 * vars: %s nick - who joined
466 * %s msg - servers message 439 * %s msg - servers message
467 * %d chan - channel joined */ 440 * %d chan - channel joined */
468static void 441static void userjoin(char *message) {
469userjoin (char *message)
470{
471 char *nick = NULL, *msg = NULL, *channel = NULL; 442 char *nick = NULL, *msg = NULL, *channel = NULL;
472 int chan = 0; 443 int chan = 0;
473 444
474 /* search start of nickname */ 445 /* search start of nickname */
475 nick = strchr (message, ' '); 446 nick = strchr(message, ' ');
476 nick++; 447 nick++;
477 448
478 /* search start of message, terminate nick */ 449 /* search start of message, terminate nick */
479 msg = strchr (nick, ' '); 450 msg = strchr(nick, ' ');
480 msg[0] = '\0'; 451 msg[0] = '\0';
481 msg++; 452 msg++;
482 453
483 /* search start of channel, terminate message */ 454 /* search start of channel, terminate message */
484 channel = strrchr (msg, ' '); 455 channel = strrchr(msg, ' ');
485 channel[0] = '\0'; 456 channel[0] = '\0';
486 channel++; 457 channel++;
487 458
488 /* convert channel to integer */ 459 /* convert channel to integer */
489 chan = atoi (channel); 460 chan = atoi(channel);
490 461
491 /* is it myself joining */ 462 /* is it myself joining */
492 if( own_nick_check(nick) ) 463 if (own_nick_check(nick))
493 own_channel_set(chan); 464 own_channel_set(chan);
494 465
495 /* notice channel join via vchat-user.c */ 466 /* notice channel join via vchat-user.c */
496 if( own_channel_get() == chan ) 467 if (own_channel_get() == chan)
497 ul_enter_chan(nick); 468 ul_enter_chan(nick);
498 469
499 /* show message to user */ 470 /* show message to user */
500 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_JOIN), nick, msg, chan); 471 snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_JOIN), nick, msg, chan);
501 writechan (tmpstr); 472 writechan(tmpstr);
502} 473}
503 474
504/* parse and handle a leave message 475/* parse and handle a leave message
@@ -506,35 +477,33 @@ userjoin (char *message)
506 * vars: %s nick - who left 477 * vars: %s nick - who left
507 * %s msg - servers message 478 * %s msg - servers message
508 * %d chan - channel joined */ 479 * %d chan - channel joined */
509static void 480static void userleave(char *message) {
510userleave (char *message)
511{
512 char *nick = NULL, *msg = NULL, *channel = NULL; 481 char *nick = NULL, *msg = NULL, *channel = NULL;
513 int chan = 0; 482 int chan = 0;
514 483
515 /* search start of nickname */ 484 /* search start of nickname */
516 nick = strchr (message, ' '); 485 nick = strchr(message, ' ');
517 nick++; 486 nick++;
518 487
519 /* search start of message, terminate nick */ 488 /* search start of message, terminate nick */
520 msg = strchr (nick, ' '); 489 msg = strchr(nick, ' ');
521 msg[0] = '\0'; 490 msg[0] = '\0';
522 msg++; 491 msg++;
523 492
524 /* convert channel to integer */ 493 /* convert channel to integer */
525 channel = strrchr (msg, ' '); 494 channel = strrchr(msg, ' ');
526 channel[0] = '\0'; 495 channel[0] = '\0';
527 channel++; 496 channel++;
528 497
529 /* convert channel to integer */ 498 /* convert channel to integer */
530 chan = atoi (channel); 499 chan = atoi(channel);
531 500
532 /* notice channel leave via vchat-user.c */ 501 /* notice channel leave via vchat-user.c */
533 ul_leave_chan(nick); 502 ul_leave_chan(nick);
534 503
535 /* show message to user */ 504 /* show message to user */
536 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_LEAVE), nick, msg, chan); 505 snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_LEAVE), nick, msg, chan);
537 writechan (tmpstr); 506 writechan(tmpstr);
538} 507}
539 508
540/* parse and handle a nickchange message 509/* parse and handle a nickchange message
@@ -542,151 +511,140 @@ userleave (char *message)
542 * vars: %s oldnick - users old nick 511 * vars: %s oldnick - users old nick
543 * %s newnick - users new nick 512 * %s newnick - users new nick
544 * %s msg - server message */ 513 * %s msg - server message */
545static void 514static void usernickchange(char *message) {
546usernickchange (char *message)
547{
548 char *oldnick = NULL, *newnick = NULL, *msg = NULL; 515 char *oldnick = NULL, *newnick = NULL, *msg = NULL;
549 516
550 /* search start of old nickname */ 517 /* search start of old nickname */
551 oldnick = strchr (message, ' '); 518 oldnick = strchr(message, ' ');
552 oldnick++; 519 oldnick++;
553 520
554 /* search start of new nickname, terminate old nick */ 521 /* search start of new nickname, terminate old nick */
555 newnick = strchr (oldnick, ' '); 522 newnick = strchr(oldnick, ' ');
556 newnick[0] = '\0'; 523 newnick[0] = '\0';
557 newnick++; 524 newnick++;
558 525
559 /* search start of message, terminate new nick */ 526 /* search start of message, terminate new nick */
560 msg = strchr (newnick, ' '); 527 msg = strchr(newnick, ' ');
561 msg[0] = '\0'; 528 msg[0] = '\0';
562 msg++; 529 msg++;
563 530
564 /* notice nickchange via vchat-user.c */ 531 /* notice nickchange via vchat-user.c */
565 ul_rename (oldnick, newnick); 532 ul_rename(oldnick, newnick);
566 533
567 if( own_nick_check(newnick) ) { 534 if (own_nick_check(newnick)) {
568 /* show change in console window */ 535 /* show change in console window */
569 snprintf (consolestr, CONSOLESTRSIZE, getformatstr(FS_CONSOLE), newnick, getstroption (CF_SERVERHOST), getstroption (CF_SERVERPORT)); 536 snprintf(consolestr, CONSOLESTRSIZE, getformatstr(FS_CONSOLE), newnick,
570 consoleline (NULL); 537 getstroption(CF_SERVERHOST), getstroption(CF_SERVERPORT));
538 consoleline(NULL);
571 } 539 }
572 540
573 /* show message to user */ 541 /* show message to user */
574 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_NICKCHANGE), oldnick, newnick, msg); 542 snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_NICKCHANGE), oldnick, newnick,
575 writechan (tmpstr); 543 msg);
544 writechan(tmpstr);
576} 545}
577 546
578/* handle received message from server */ 547/* handle received message from server */
579void 548void protocol_parsemsg(char *message) {
580protocol_parsemsg (char *message)
581{
582 char *str1, *str2; 549 char *str1, *str2;
583 int i; 550 int i;
584 /* message to short or starts with '<'? must be channel */ 551 /* message to short or starts with '<'? must be channel */
585 if (message[0] == '<') 552 if (message[0] == '<') {
586 {
587 str1 = &message[1]; 553 str1 = &message[1];
588 str2 = strchr(str1,'>'); 554 str2 = strchr(str1, '>');
589 str2[0] = '\0'; 555 str2[0] = '\0';
590 str2++; 556 str2++;
591 if (str2[0] == ' ') str2++; 557 if (str2[0] == ' ')
558 str2++;
592 if (own_nick_check(str1)) 559 if (own_nick_check(str1))
593 snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_MYPUBMSG),str1,str2); 560 snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_MYPUBMSG), str1, str2);
594 else 561 else
595 snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_RXPUBMSG),str1,str2); 562 snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_RXPUBMSG), str1, str2);
596 writechan (tmpstr); 563 writechan(tmpstr);
597 564
598 ul_public_action(str1); 565 ul_public_action(str1);
599 } 566 } else if (message[0] == '[') {
600 else if (message[0] == '[')
601 {
602 str1 = &message[1]; 567 str1 = &message[1];
603 str2 = strchr(str1,']'); 568 str2 = strchr(str1, ']');
604 str2[0] = '\0'; 569 str2[0] = '\0';
605 str2++; 570 str2++;
606 if (str2[0] == ' ') str2++; 571 if (str2[0] == ' ')
607 if (own_nick_check( str1 )) 572 str2++;
608 snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_MYPUBURL),str1,str2); 573 if (own_nick_check(str1))
574 snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_MYPUBURL), str1, str2);
609 else 575 else
610 snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_RXPUBURL),str1,str2); 576 snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_RXPUBURL), str1, str2);
611 ul_public_action(str1); 577 ul_public_action(str1);
612 writechan (tmpstr); 578 writechan(tmpstr);
613 } 579 }
614 /* message starts with '*'? must be private */ 580 /* message starts with '*'? must be private */
615 else if (message[0] == '*') 581 else if (message[0] == '*') {
616 {
617 str1 = &message[1]; 582 str1 = &message[1];
618 str2 = strchr(str1,'*'); 583 str2 = strchr(str1, '*');
619 str2[0] = '\0'; 584 str2[0] = '\0';
620 str2++; 585 str2++;
621 if (str2[0] == ' ') str2++; 586 if (str2[0] == ' ')
622 snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_RXPRIVMSG),str1,str2); 587 str2++;
623 writepriv (tmpstr, 1); 588 snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_RXPRIVMSG), str1, str2);
589 writepriv(tmpstr, 1);
624 ul_private_action(str1); 590 ul_private_action(str1);
625 } 591 }
626 /* message starts with a number? must be a servermessage */ 592 /* message starts with a number? must be a servermessage */
627 else if ((message[0] >= '0') && (message[0] <= '9')) 593 else if ((message[0] >= '0') && (message[0] <= '9')) {
628 { 594 /* walk server message array */
629 /* walk server message array */ 595 for (i = 0; servermessages[i].id[0]; i++) {
630 for (i = 0; servermessages[i].id[0]; i++) 596 /* is this the message? */
631 { 597 if (!(strncmp(servermessages[i].id, message, 3))) {
632 /* is this the message? */ 598 /* scripting hook available - call it */
633 if (!(strncmp (servermessages[i].id, message, 3))) 599 if (servermessages[i].hook)
634 { 600 servermessages[i].hook(message);
635 /* scripting hook available - call it */ 601 /* function available - call it */
636 if (servermessages[i].hook) 602 else if (servermessages[i].funct)
637 servermessages[i].hook (message); 603 servermessages[i].funct(message);
638 /* function available - call it */ 604 /* no function available, but known - give output */
639 else if (servermessages[i].funct) 605 else {
640 servermessages[i].funct (message); 606 /* remove continutation mark */
641 /* no function available, but known - give output */ 607 if (message[3] == '-')
642 else 608 message[3] = ' ';
643 { 609
644 /* remove continutation mark */ 610 /* mutate message for display */
645 if (message[3] == '-') 611 message[2] = '*';
646 message[3] = ' '; 612 /* giveout message by type */
647 613 switch (servermessages[i].type) {
648 /* mutate message for display */ 614 case SM_IGNORE:
649 message[2] = '*'; 615 break;
650 /* giveout message by type */ 616 case SM_INFO:
651 switch (servermessages[i].type) 617 /* change marker and send as servermessage */
652 { 618 message[2] = '#';
653 case SM_IGNORE: 619 writecf(FS_SERV, &message[2]);
654 break; 620 break;
655 case SM_INFO: 621 case SM_USERINFO:
656 /* change marker and send as servermessage */ 622 /* keep marker and send as servermessage */
657 message[2] = '#'; 623 writecf(FS_SERV, &message[2]);
658 writecf (FS_SERV,&message[2]); 624 break;
659 break; 625 case SM_CHANNEL:
660 case SM_USERINFO: 626 /* keep marker and send as channelmessage */
661 /* keep marker and send as servermessage */ 627 writechan(&message[2]);
662 writecf (FS_SERV,&message[2]); 628 break;
663 break; 629 case SM_ERROR:
664 case SM_CHANNEL: 630 /* change marker and send as errormessage */
665 /* keep marker and send as channelmessage */ 631 message[2] = '!';
666 writechan (&message[2]); 632 writecf(FS_ERR, &message[2]);
667 break; 633 break;
668 case SM_ERROR: 634 default:
669 /* change marker and send as errormessage */ 635 /* fallback: keep marker and send as servermessage */
670 message[2] = '!'; 636 writecf(FS_SERV, &message[2]);
671 writecf (FS_ERR,&message[2]); 637 }
672 break; 638 }
673 default: 639 return;
674 /* fallback: keep marker and send as servermessage */ 640 }
675 writecf (FS_SERV,&message[2]);
676 }
677 }
678 return;
679 }
680 }
681 /* message not in list, raise errormessage */
682 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_UNKNOWNMSG), message);
683 writecf (FS_ERR,tmpstr);
684 }
685 else
686 {
687 /* message neither public, private or server, raise errormessage */
688 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_BOGUSMSG), message);
689 writecf (FS_ERR,tmpstr);
690 } 641 }
642 /* message not in list, raise errormessage */
643 snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_UNKNOWNMSG), message);
644 writecf(FS_ERR, tmpstr);
645 } else {
646 /* message neither public, private or server, raise errormessage */
647 snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_BOGUSMSG), message);
648 writecf(FS_ERR, tmpstr);
649 }
691} 650}
692
diff --git a/vchat-tls.c b/vchat-tls.c
index 7b0f1fa..187f10b 100755
--- a/vchat-tls.c
+++ b/vchat-tls.c
@@ -15,663 +15,703 @@
15 * 15 *
16 */ 16 */
17 17
18#include <assert.h>
19#include <errno.h>
18#include <stdio.h> 20#include <stdio.h>
19#include <stdlib.h> 21#include <stdlib.h>
20#include <string.h> 22#include <string.h>
21#include <assert.h>
22#include <errno.h>
23 23
24#include <readline/readline.h> 24#include <readline/readline.h>
25 25
26#include "vchat.h"
27#include "vchat-tls.h" 26#include "vchat-tls.h"
27#include "vchat.h"
28 28
29const char *vchat_tls_version = "vchat-tls.c $Id$"; 29const char *vchat_tls_version =
30const char *vchat_tls_version_external = "Unknown implementation; version unknown"; 30 "vchat-tls.c $Id$";
31const char *vchat_tls_version_external =
32 "Unknown implementation; version unknown";
31 33
32/* Helpers to work with vc_x509store_t used by all tls libs */ 34/* Helpers to work with vc_x509store_t used by all tls libs */
33void vc_cleanup_x509store(vc_x509store_t *store) { 35void vc_cleanup_x509store(vc_x509store_t *store) {
34 free(store->cafile); 36 free(store->cafile);
35 free(store->capath); 37 free(store->capath);
36 free(store->crlfile); 38 free(store->crlfile);
37 free(store->certfile); 39 free(store->certfile);
38 free(store->keyfile); 40 free(store->keyfile);
39 memset(store, 0, sizeof(vc_x509store_t)); 41 memset(store, 0, sizeof(vc_x509store_t));
40} 42}
41 43
42void vc_x509store_setflags(vc_x509store_t *store, int flags) { store->flags |= flags; } 44void vc_x509store_setflags(vc_x509store_t *store, int flags) {
43void vc_x509store_clearflags(vc_x509store_t *store, int flags) { store->flags &= ~flags; } 45 store->flags |= flags;
44void vc_x509store_set_pkeycb(vc_x509store_t *store, vc_askpass_cb_t callback) { store->askpass_callback = callback; } 46}
47void vc_x509store_clearflags(vc_x509store_t *store, int flags) {
48 store->flags &= ~flags;
49}
50void vc_x509store_set_pkeycb(vc_x509store_t *store, vc_askpass_cb_t callback) {
51 store->askpass_callback = callback;
52}
45 53
46void vc_x509store_setcafile(vc_x509store_t *store, char *file) { 54void vc_x509store_setcafile(vc_x509store_t *store, char *file) {
47 free(store->cafile); 55 free(store->cafile);
48 store->cafile = ( file ? strdup(file) : 0 ); 56 store->cafile = (file ? strdup(file) : 0);
49 store->flags |= VC_X509S_USE_CAFILE; 57 store->flags |= VC_X509S_USE_CAFILE;
50} 58}
51 59
52void vc_x509store_setcapath(vc_x509store_t *store, char *path) { 60void vc_x509store_setcapath(vc_x509store_t *store, char *path) {
53 free(store->capath); 61 free(store->capath);
54 store->capath = ( path ? strdup(path) : 0 ); 62 store->capath = (path ? strdup(path) : 0);
55} 63}
56 64
57void vc_x509store_setcrlfile(vc_x509store_t *store, char *file) { 65void vc_x509store_setcrlfile(vc_x509store_t *store, char *file) {
58 free(store->crlfile); 66 free(store->crlfile);
59 store->crlfile = ( file ? strdup(file) : 0 ); 67 store->crlfile = (file ? strdup(file) : 0);
60} 68}
61 69
62void vc_x509store_setkeyfile(vc_x509store_t *store, char *file) { 70void vc_x509store_setkeyfile(vc_x509store_t *store, char *file) {
63 free(store->keyfile); 71 free(store->keyfile);
64 store->keyfile = ( file ? strdup(file) : 0 ); 72 store->keyfile = (file ? strdup(file) : 0);
65} 73}
66 74
67void vc_x509store_setcertfile(vc_x509store_t *store, char *file) { 75void vc_x509store_setcertfile(vc_x509store_t *store, char *file) {
68 free(store->certfile); 76 free(store->certfile);
69 store->certfile = ( file ? strdup(file) : 0 ); 77 store->certfile = (file ? strdup(file) : 0);
70 store->flags |= VC_X509S_USE_CERTIFICATE; 78 store->flags |= VC_X509S_USE_CERTIFICATE;
71} 79}
72 80
73static int verify_or_store_fingerprint(const char *fingerprint) { 81static int verify_or_store_fingerprint(const char *fingerprint) {
74 char *fingerprint_file_path = tilde_expand(getstroption(CF_FINGERPRINT)); 82 char *fingerprint_file_path = tilde_expand(getstroption(CF_FINGERPRINT));
75 if (!fingerprint_file_path) { 83 if (!fingerprint_file_path) {
76 writecf(FS_ERR, "Error: The CF_FINGERPRINT path is not set but CF_PINFINGER was requested."); 84 writecf(FS_ERR, "Error: The CF_FINGERPRINT path is not set but "
77 return -1; 85 "CF_PINFINGER was requested.");
86 return -1;
87 }
88
89 FILE *fingerprint_file = fopen(fingerprint_file_path, "r");
90 if (fingerprint_file) {
91 /* Read fingerprint from file */
92 char old_fingerprint[128];
93 char *r = fgets(old_fingerprint, sizeof(old_fingerprint), fingerprint_file);
94 fclose(fingerprint_file);
95
96 if (r) {
97 /* chomp */
98 char *nl = strchr(r, '\n');
99 if (nl)
100 *nl = 0;
101
102 /* verify fingerprint matches stored version */
103 if (!strcmp(fingerprint, old_fingerprint)) {
104 writecf(FS_SERV, "[FINGERPRINT MATCH ]");
105 goto cleanup_happy;
106 }
78 } 107 }
79 108
80 FILE *fingerprint_file = fopen(fingerprint_file_path, "r"); 109 snprintf(tmpstr, TMPSTRSIZE,
81 if (fingerprint_file) { 110 "Error: Found pinned fingerprint (in %s) %s but expected %s",
82 /* Read fingerprint from file */ 111 r ? old_fingerprint : "<FILE READ ERROR>",
83 char old_fingerprint[128]; 112 getstroption(CF_FINGERPRINT), fingerprint);
84 char * r = fgets(old_fingerprint, sizeof(old_fingerprint), fingerprint_file); 113 writecf(FS_ERR, tmpstr);
85 fclose(fingerprint_file); 114 writecf(FS_ERR, "Error: Fingerprint mismatch! Server cert updated?");
86
87 if (r) {
88 /* chomp */
89 char *nl = strchr(r, '\n');
90 if (nl) *nl = 0;
91
92 /* verify fingerprint matches stored version */
93 if (!strcmp(fingerprint, old_fingerprint)) {
94 writecf(FS_SERV, "[FINGERPRINT MATCH ]");
95 goto cleanup_happy;
96 }
97 }
98
99 snprintf(tmpstr, TMPSTRSIZE, "Error: Found pinned fingerprint (in %s) %s but expected %s", r ? old_fingerprint : "<FILE READ ERROR>", getstroption(CF_FINGERPRINT), fingerprint);
100 writecf(FS_ERR, tmpstr);
101 writecf(FS_ERR, "Error: Fingerprint mismatch! Server cert updated?");
102 free(fingerprint_file_path);
103 return 1;
104 } else
105 writecf(FS_ERR, "Warning: No pinned fingerprint found, writing the current one.");
106
107 fingerprint_file = fopen(fingerprint_file_path, "w");
108 if (!fingerprint_file) {
109 snprintf (tmpstr, TMPSTRSIZE, "Warning: Can't write fingerprint file, %s.", strerror(errno));
110 writecf(FS_ERR, tmpstr);
111 } else {
112 fputs(fingerprint, fingerprint_file);
113 fclose(fingerprint_file);
114 writecf(FS_SERV, "[FINGERPRINT STORED ]");
115 }
116cleanup_happy:
117 free(fingerprint_file_path); 115 free(fingerprint_file_path);
118 return 0; 116 return 1;
117 } else
118 writecf(FS_ERR,
119 "Warning: No pinned fingerprint found, writing the current one.");
120
121 fingerprint_file = fopen(fingerprint_file_path, "w");
122 if (!fingerprint_file) {
123 snprintf(tmpstr, TMPSTRSIZE, "Warning: Can't write fingerprint file, %s.",
124 strerror(errno));
125 writecf(FS_ERR, tmpstr);
126 } else {
127 fputs(fingerprint, fingerprint_file);
128 fclose(fingerprint_file);
129 writecf(FS_SERV, "[FINGERPRINT STORED ]");
130 }
131cleanup_happy:
132 free(fingerprint_file_path);
133 return 0;
119} 134}
120 135
121#if defined(TLS_LIB_OPENSSL) && defined(TLS_LIB_MBEDTLS) 136#if defined(TLS_LIB_OPENSSL) && defined(TLS_LIB_MBEDTLS)
122#error "Both TLS_LIB_OPENSSL and TLS_LIB_MBEDTLS are defined. Please select only one." 137#error \
138 "Both TLS_LIB_OPENSSL and TLS_LIB_MBEDTLS are defined. Please select only one."
123#endif 139#endif
124#if !defined(TLS_LIB_OPENSSL) && !defined(TLS_LIB_MBEDTLS) 140#if !defined(TLS_LIB_OPENSSL) && !defined(TLS_LIB_MBEDTLS)
125#error "Neither TLS_LIB_OPENSSL nor TLS_LIB_MBEDTLS are defined. Please select exactly one." 141#error \
142 "Neither TLS_LIB_OPENSSL nor TLS_LIB_MBEDTLS are defined. Please select exactly one."
126#endif 143#endif
127 144
128#ifdef TLS_LIB_OPENSSL 145#ifdef TLS_LIB_OPENSSL
129 146
130#include <openssl/err.h>
131#include <openssl/ssl.h>
132#include <openssl/bio.h> 147#include <openssl/bio.h>
148#include <openssl/conf.h>
149#include <openssl/err.h>
133#include <openssl/evp.h> 150#include <openssl/evp.h>
151#include <openssl/ssl.h>
134#include <openssl/x509.h> 152#include <openssl/x509.h>
135#include <openssl/x509v3.h> 153#include <openssl/x509v3.h>
136#include <openssl/conf.h>
137 154
138void vchat_tls_get_version_external() 155void vchat_tls_get_version_external() {
139{ 156 snprintf(tmpstr, sizeof(tmpstr), "OpenSSL %s with %s",
140 snprintf(tmpstr, sizeof(tmpstr), "OpenSSL %s with %s", SSLeay_version(SSLEAY_VERSION), SSLeay_version(SSLEAY_CFLAGS)); 157 SSLeay_version(SSLEAY_VERSION), SSLeay_version(SSLEAY_CFLAGS));
141 vchat_tls_version_external = strdup(tmpstr); 158 vchat_tls_version_external = strdup(tmpstr);
142} 159}
143 160
144void vc_init_x509store(vc_x509store_t *store) 161void vc_init_x509store(vc_x509store_t *store) {
145{ 162 static int sslinit;
146 static int sslinit; 163 if (!sslinit++) {
147 if (!sslinit++) { 164 SSL_library_init();
148 SSL_library_init (); 165 SSL_load_error_strings();
149 SSL_load_error_strings(); 166 }
150 } 167 memset(store, 0, sizeof(vc_x509store_t));
151 memset(store, 0, sizeof(vc_x509store_t));
152 168
153 /* We want to make verifying the peer the default */ 169 /* We want to make verifying the peer the default */
154 store->flags |= VC_X509S_SSL_VERIFY_PEER; 170 store->flags |= VC_X509S_SSL_VERIFY_PEER;
155} 171}
156 172
157/* connection BIO for openssl */ 173/* connection BIO for openssl */
158static BIO *server_conn = NULL; 174static BIO *server_conn = NULL;
159 175
160static SSL_CTX * vc_create_sslctx( vc_x509store_t *vc_store ); 176static SSL_CTX *vc_create_sslctx(vc_x509store_t *vc_store);
161static int vc_verify_callback(int, X509_STORE_CTX *); 177static int vc_verify_callback(int, X509_STORE_CTX *);
162static X509_STORE * vc_x509store_create(vc_x509store_t *); 178static X509_STORE *vc_x509store_create(vc_x509store_t *);
163
164static SSL_CTX * vc_create_sslctx( vc_x509store_t *vc_store )
165{
166 int flags = 0;
167
168 /* Explicitly use TLSv1 (or maybe later) */
169 SSL_CTX *ctx = SSL_CTX_new(TLS_client_method());
170 X509_STORE *store = vc_x509store_create(vc_store);
171
172 if (!ctx || !store) {
173 snprintf(tmpstr, sizeof(tmpstr), "CREATE CTX: %s",ERR_error_string (ERR_get_error (), NULL));
174 writecf(FS_ERR, tmpstr);
175 if (store)
176 X509_STORE_free(store);
177 if (ctx)
178 SSL_CTX_free(ctx);
179 return NULL;
180 }
181
182 SSL_CTX_set_cert_store(ctx, store);
183
184 /* Disable some insecure protocols explicitly */
185 SSL_CTX_set_options(ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
186 if (getstroption(CF_CIPHERSUITE))
187 SSL_CTX_set_cipher_list(ctx, getstroption(CF_CIPHERSUITE));
188 else
189 SSL_CTX_set_cipher_list(ctx, "ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA");
190
191 SSL_CTX_set_verify_depth (ctx, getintoption(CF_VERIFYSSL));
192
193 if( !(vc_store->flags & VC_X509S_SSL_VERIFY_MASK) ) {
194 writecf(FS_DBG, tmpstr);
195 flags = SSL_VERIFY_NONE;
196 } else {
197 if(vc_store->flags & VC_X509S_SSL_VERIFY_PEER)
198 flags |= SSL_VERIFY_PEER;
199 if(vc_store->flags & VC_X509S_SSL_VERIFY_FAIL_IF_NO_PEER_CERT)
200 flags |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
201 if(vc_store->flags & VC_X509S_SSL_VERIFY_CLIENT_ONCE)
202 flags |= SSL_VERIFY_CLIENT_ONCE;
203 }
204
205 SSL_CTX_set_verify(ctx, flags, vc_verify_callback);
206
207 if(vc_store->flags & VC_X509S_USE_CERTIFICATE) {
208 int r = 0;
209 if(vc_store->certfile)
210 SSL_CTX_use_certificate_chain_file(ctx, vc_store->certfile);
211 179
212 SSL_CTX_set_default_passwd_cb(ctx, vc_store->askpass_callback); 180static SSL_CTX *vc_create_sslctx(vc_x509store_t *vc_store) {
181 int flags = 0;
213 182
214 if(vc_store->keyfile) 183 /* Explicitly use TLSv1 (or maybe later) */
215 r=SSL_CTX_use_PrivateKey_file(ctx, vc_store->keyfile, 184 SSL_CTX *ctx = SSL_CTX_new(TLS_client_method());
216 SSL_FILETYPE_PEM); 185 X509_STORE *store = vc_x509store_create(vc_store);
217 186
218 if( r!=1 || !SSL_CTX_check_private_key(ctx)) { 187 if (!ctx || !store) {
219 snprintf(tmpstr, sizeof(tmpstr), "CREATE CTX: Load private key failed"); 188 snprintf(tmpstr, sizeof(tmpstr), "CREATE CTX: %s",
220 writecf(FS_ERR, tmpstr); 189 ERR_error_string(ERR_get_error(), NULL));
221 SSL_CTX_free(ctx); 190 writecf(FS_ERR, tmpstr);
222 return NULL; 191 if (store)
223 } 192 X509_STORE_free(store);
193 if (ctx)
194 SSL_CTX_free(ctx);
195 return NULL;
196 }
197
198 SSL_CTX_set_cert_store(ctx, store);
199
200 /* Disable some insecure protocols explicitly */
201 SSL_CTX_set_options(ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
202 if (getstroption(CF_CIPHERSUITE))
203 SSL_CTX_set_cipher_list(ctx, getstroption(CF_CIPHERSUITE));
204 else
205 SSL_CTX_set_cipher_list(ctx,
206 "ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA");
207
208 SSL_CTX_set_verify_depth(ctx, getintoption(CF_VERIFYSSL));
209
210 if (!(vc_store->flags & VC_X509S_SSL_VERIFY_MASK)) {
211 writecf(FS_DBG, tmpstr);
212 flags = SSL_VERIFY_NONE;
213 } else {
214 if (vc_store->flags & VC_X509S_SSL_VERIFY_PEER)
215 flags |= SSL_VERIFY_PEER;
216 if (vc_store->flags & VC_X509S_SSL_VERIFY_FAIL_IF_NO_PEER_CERT)
217 flags |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
218 if (vc_store->flags & VC_X509S_SSL_VERIFY_CLIENT_ONCE)
219 flags |= SSL_VERIFY_CLIENT_ONCE;
220 }
221
222 SSL_CTX_set_verify(ctx, flags, vc_verify_callback);
223
224 if (vc_store->flags & VC_X509S_USE_CERTIFICATE) {
225 int r = 0;
226 if (vc_store->certfile)
227 SSL_CTX_use_certificate_chain_file(ctx, vc_store->certfile);
228
229 SSL_CTX_set_default_passwd_cb(ctx, vc_store->askpass_callback);
230
231 if (vc_store->keyfile)
232 r = SSL_CTX_use_PrivateKey_file(ctx, vc_store->keyfile, SSL_FILETYPE_PEM);
233
234 if (r != 1 || !SSL_CTX_check_private_key(ctx)) {
235 snprintf(tmpstr, sizeof(tmpstr), "CREATE CTX: Load private key failed");
236 writecf(FS_ERR, tmpstr);
237 SSL_CTX_free(ctx);
238 return NULL;
224 } 239 }
240 }
225 241
226 SSL_CTX_set_app_data(ctx, vc_store); 242 SSL_CTX_set_app_data(ctx, vc_store);
227 return(ctx); 243 return (ctx);
228} 244}
229 245
230int vc_tls_connect( int serverfd, vc_x509store_t *vc_store ) 246int vc_tls_connect(int serverfd, vc_x509store_t *vc_store) {
231{ 247 SSL_CTX *ctx = vc_create_sslctx(vc_store);
232 SSL_CTX * ctx = vc_create_sslctx(vc_store); 248 X509 *peercert = NULL;
233 X509 *peercert = NULL; 249 BIO *ssl_conn = NULL;
234 BIO *ssl_conn = NULL; 250 const SSL *sslp = NULL;
235 const SSL *sslp = NULL; 251 const SSL_CIPHER *cipher = NULL;
236 const SSL_CIPHER * cipher = NULL;
237
238 server_conn = BIO_new_socket( serverfd, 1 );
239
240 /* To display and check server fingerprint */
241 char fingerprint[EVP_MAX_MD_SIZE*4];
242 unsigned char fingerprint_bin[EVP_MAX_MD_SIZE];
243 unsigned int fingerprint_len;
244 252
245 char * fp = fingerprint; 253 server_conn = BIO_new_socket(serverfd, 1);
246 254
247 long j; 255 /* To display and check server fingerprint */
256 char fingerprint[EVP_MAX_MD_SIZE * 4];
257 unsigned char fingerprint_bin[EVP_MAX_MD_SIZE];
258 unsigned int fingerprint_len;
248 259
249 if( !ctx ) 260 char *fp = fingerprint;
250 goto all_errors;
251 261
252 ssl_conn = BIO_new_ssl(ctx, 1); 262 long j;
253 SSL_CTX_free(ctx);
254 263
255 if( !ssl_conn ) 264 if (!ctx)
256 goto ssl_error; 265 goto all_errors;
257 266
258 BIO_push( ssl_conn, server_conn ); 267 ssl_conn = BIO_new_ssl(ctx, 1);
259 server_conn = ssl_conn; 268 SSL_CTX_free(ctx);
260 fflush(stdout);
261 269
262 if( BIO_do_handshake( server_conn ) <= 0 ) 270 if (!ssl_conn)
263 goto ssl_error; 271 goto ssl_error;
264 272
265 /* Show information about cipher used */ 273 BIO_push(ssl_conn, server_conn);
266 /* Get cipher object */ 274 server_conn = ssl_conn;
267 BIO_get_ssl(ssl_conn, &sslp); 275 fflush(stdout);
268 if (!sslp)
269 goto ssl_error;
270 276
271 cipher = SSL_get_current_cipher(sslp); 277 if (BIO_do_handshake(server_conn) <= 0)
272 if (cipher) { 278 goto ssl_error;
273 char cipher_desc[TMPSTRSIZE];
274 snprintf(tmpstr, TMPSTRSIZE, "[SSL CIPHER ] %s", SSL_CIPHER_description(cipher, cipher_desc, TMPSTRSIZE));
275 writecf(FS_SERV, tmpstr);
276 } else {
277 snprintf(tmpstr, TMPSTRSIZE, "[SSL ERROR ] Cipher not known / SSL object can't be queried!");
278 writecf(FS_ERR, tmpstr);
279 }
280 279
281 /* Accept being connected, _if_ verification passed */ 280 /* Show information about cipher used */
282 peercert = SSL_get_peer_certificate(sslp); 281 /* Get cipher object */
283 if (!peercert) 282 BIO_get_ssl(ssl_conn, &sslp);
284 goto ssl_error; 283 if (!sslp)
284 goto ssl_error;
285 285
286 /* show basic information about peer cert */ 286 cipher = SSL_get_current_cipher(sslp);
287 snprintf(tmpstr, TMPSTRSIZE, "[SSL SUBJECT ] %s", X509_NAME_oneline(X509_get_subject_name(peercert),0,0)); 287 if (cipher) {
288 writecf(FS_SERV, tmpstr); 288 char cipher_desc[TMPSTRSIZE];
289 snprintf(tmpstr, TMPSTRSIZE, "[SSL ISSUER ] %s", X509_NAME_oneline(X509_get_issuer_name(peercert),0,0)); 289 snprintf(tmpstr, TMPSTRSIZE, "[SSL CIPHER ] %s",
290 SSL_CIPHER_description(cipher, cipher_desc, TMPSTRSIZE));
290 writecf(FS_SERV, tmpstr); 291 writecf(FS_SERV, tmpstr);
292 } else {
293 snprintf(
294 tmpstr, TMPSTRSIZE,
295 "[SSL ERROR ] Cipher not known / SSL object can't be queried!");
296 writecf(FS_ERR, tmpstr);
297 }
298
299 /* Accept being connected, _if_ verification passed */
300 peercert = SSL_get_peer_certificate(sslp);
301 if (!peercert)
302 goto ssl_error;
303
304 /* show basic information about peer cert */
305 snprintf(tmpstr, TMPSTRSIZE, "[SSL SUBJECT ] %s",
306 X509_NAME_oneline(X509_get_subject_name(peercert), 0, 0));
307 writecf(FS_SERV, tmpstr);
308 snprintf(tmpstr, TMPSTRSIZE, "[SSL ISSUER ] %s",
309 X509_NAME_oneline(X509_get_issuer_name(peercert), 0, 0));
310 writecf(FS_SERV, tmpstr);
311
312 /* calculate fingerprint */
313 if (!X509_digest(peercert, EVP_sha1(), fingerprint_bin, &fingerprint_len))
314 goto ssl_error;
315
316 assert((fingerprint_len > 1) && (fingerprint_len <= EVP_MAX_MD_SIZE));
317 for (j = 0; j < (int)fingerprint_len; j++)
318 fp += sprintf(fp, "%02X:", fingerprint_bin[j]);
319 assert(fp > fingerprint);
320 fp[-1] = 0;
321 snprintf(tmpstr, TMPSTRSIZE, "[SSL FINGERPRINT ] %s (from server)",
322 fingerprint);
323 writecf(FS_SERV, tmpstr);
324
325 /* we don't need the peercert anymore */
326 X509_free(peercert);
327
328 if (getintoption(CF_PINFINGER) && verify_or_store_fingerprint(fingerprint))
329 return 1;
291 330
292 /* calculate fingerprint */ 331 /* If verify of x509 chain was requested, do the check here */
293 if (!X509_digest(peercert,EVP_sha1(),fingerprint_bin,&fingerprint_len)) 332 if (X509_V_OK == SSL_get_verify_result(sslp))
294 goto ssl_error; 333 return 0;
295
296 assert ( ( fingerprint_len > 1 ) && (fingerprint_len <= EVP_MAX_MD_SIZE ));
297 for (j=0; j<(int)fingerprint_len; j++)
298 fp += sprintf(fp, "%02X:", fingerprint_bin[j]);
299 assert ( fp > fingerprint );
300 fp[-1] = 0;
301 snprintf(tmpstr, TMPSTRSIZE, "[SSL FINGERPRINT ] %s (from server)", fingerprint);
302 writecf(FS_SERV, tmpstr);
303
304 /* we don't need the peercert anymore */
305 X509_free(peercert);
306
307 if (getintoption(CF_PINFINGER) && verify_or_store_fingerprint(fingerprint))
308 return 1;
309
310 /* If verify of x509 chain was requested, do the check here */
311 if (X509_V_OK == SSL_get_verify_result(sslp))
312 return 0;
313 334
314 if (getintoption(CF_IGNSSL)) { 335 if (getintoption(CF_IGNSSL)) {
315 writecf(FS_ERR, "[SSL VERIFY ERROR ] FAILURE IGNORED!!!"); 336 writecf(FS_ERR, "[SSL VERIFY ERROR ] FAILURE IGNORED!!!");
316 return 0; 337 return 0;
317 } 338 }
318 339
319ssl_error: 340ssl_error:
320 snprintf(tmpstr, TMPSTRSIZE, "[SSL CONNECT ERROR] %s", ERR_error_string (ERR_get_error (), NULL)); 341 snprintf(tmpstr, TMPSTRSIZE, "[SSL CONNECT ERROR] %s",
321 writecf(FS_ERR, tmpstr); 342 ERR_error_string(ERR_get_error(), NULL));
343 writecf(FS_ERR, tmpstr);
322all_errors: 344all_errors:
323 BIO_free_all( server_conn ); 345 BIO_free_all(server_conn);
324 server_conn = NULL; 346 server_conn = NULL;
325 return 1; 347 return 1;
326} 348}
327 349
328#define VC_STORE_ERR_EXIT(s) do { \ 350#define VC_STORE_ERR_EXIT(s) \
329 fprintf(stderr, "[E] SSL_STORE: %s\n", ERR_error_string (ERR_get_error (), NULL)); \ 351 do { \
330 if(s) X509_STORE_free(s); \ 352 fprintf(stderr, "[E] SSL_STORE: %s\n", \
331 return(0); \ 353 ERR_error_string(ERR_get_error(), NULL)); \
332} while(0) 354 if (s) \
355 X509_STORE_free(s); \
356 return (0); \
357 } while (0)
333 358
334X509_STORE *vc_x509store_create(vc_x509store_t *vc_store) { 359X509_STORE *vc_x509store_create(vc_x509store_t *vc_store) {
335 X509_STORE *store = NULL; 360 X509_STORE *store = NULL;
336 X509_LOOKUP *lookup = NULL; 361 X509_LOOKUP *lookup = NULL;
337 362
338 store = X509_STORE_new(); 363 store = X509_STORE_new();
339 364
340 X509_STORE_set_verify_cb_func(store, vc_verify_callback); 365 X509_STORE_set_verify_cb_func(store, vc_verify_callback);
341 366
342 if( !(lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file())) ) 367 if (!(lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file())))
343 VC_STORE_ERR_EXIT(store); 368 VC_STORE_ERR_EXIT(store);
344 369
345 if (!vc_store->cafile) { 370 if (!vc_store->cafile) {
346 if( !(vc_store->flags & VC_X509S_USE_CAFILE) ) 371 if (!(vc_store->flags & VC_X509S_USE_CAFILE))
347 X509_LOOKUP_load_file(lookup, 0, X509_FILETYPE_DEFAULT); 372 X509_LOOKUP_load_file(lookup, 0, X509_FILETYPE_DEFAULT);
348 } else if( !X509_LOOKUP_load_file(lookup, vc_store->cafile, 373 } else if (!X509_LOOKUP_load_file(lookup, vc_store->cafile,
349 X509_FILETYPE_PEM) ) 374 X509_FILETYPE_PEM))
350 VC_STORE_ERR_EXIT(store); 375 VC_STORE_ERR_EXIT(store);
351 376
352 if (vc_store->crlfile) { 377 if (vc_store->crlfile) {
353 if( !X509_load_crl_file(lookup, vc_store->crlfile, 378 if (!X509_load_crl_file(lookup, vc_store->crlfile, X509_FILETYPE_PEM))
354 X509_FILETYPE_PEM) ) 379 VC_STORE_ERR_EXIT(store);
355 VC_STORE_ERR_EXIT(store);
356 380
357 X509_STORE_set_flags( store, 381 X509_STORE_set_flags(store,
358 X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL ); 382 X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
359 } 383 }
360 384
361 if ( !(lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir())) ) 385 if (!(lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir())))
362 VC_STORE_ERR_EXIT(store); 386 VC_STORE_ERR_EXIT(store);
363 387
364 if ( !vc_store->capath ) { 388 if (!vc_store->capath) {
365 if( !(vc_store->flags & VC_X509S_USE_CAPATH) ) 389 if (!(vc_store->flags & VC_X509S_USE_CAPATH))
366 X509_LOOKUP_add_dir(lookup, 0, X509_FILETYPE_DEFAULT); 390 X509_LOOKUP_add_dir(lookup, 0, X509_FILETYPE_DEFAULT);
367 } else if( !X509_LOOKUP_add_dir(lookup, vc_store->capath, 391 } else if (!X509_LOOKUP_add_dir(lookup, vc_store->capath, X509_FILETYPE_PEM))
368 X509_FILETYPE_PEM) ) 392 VC_STORE_ERR_EXIT(store);
369 VC_STORE_ERR_EXIT(store);
370 393
371 return(store); 394 return (store);
372} 395}
373 396
374int vc_verify_callback(int ok, X509_STORE_CTX *store) { 397int vc_verify_callback(int ok, X509_STORE_CTX *store) {
375 if(!ok) { 398 if (!ok) {
376 snprintf(tmpstr, TMPSTRSIZE, "[SSL VERIFY ERROR ] %s", 399 snprintf(tmpstr, TMPSTRSIZE, "[SSL VERIFY ERROR ] %s",
377 X509_verify_cert_error_string(X509_STORE_CTX_get_error(store))); 400 X509_verify_cert_error_string(X509_STORE_CTX_get_error(store)));
378 writecf(FS_ERR, tmpstr); 401 writecf(FS_ERR, tmpstr);
379 } 402 }
380 return (ok | getintoption(CF_IGNSSL)); 403 return (ok | getintoption(CF_IGNSSL));
381} 404}
382 405
383ssize_t vc_tls_sendmessage(const void *buf, size_t size) { 406ssize_t vc_tls_sendmessage(const void *buf, size_t size) {
384 return BIO_write(server_conn, buf, size); 407 return BIO_write(server_conn, buf, size);
385} 408}
386 409
387ssize_t vc_tls_receivemessage(void *buf, size_t size) { 410ssize_t vc_tls_receivemessage(void *buf, size_t size) {
388 ssize_t received = (ssize_t)BIO_read (server_conn, buf, size); 411 ssize_t received = (ssize_t)BIO_read(server_conn, buf, size);
389 if (received != 0) 412 if (received != 0)
390 return received; 413 return received;
391 if (BIO_should_retry(server_conn)) 414 if (BIO_should_retry(server_conn))
392 return -2; 415 return -2;
393 return 0; 416 return 0;
394} 417}
395 418
396void vc_tls_cleanup() { 419void vc_tls_cleanup() {
397 BIO_free_all( server_conn ); 420 BIO_free_all(server_conn);
398 server_conn = NULL; 421 server_conn = NULL;
399} 422}
400#endif 423#endif
401 424
402#ifdef TLS_LIB_MBEDTLS 425#ifdef TLS_LIB_MBEDTLS
403 426
427#include "mbedtls/error.h"
428#include "mbedtls/version.h"
429#include <mbedtls/ctr_drbg.h>
430#include <mbedtls/debug.h>
431#include <mbedtls/entropy.h>
432#include <mbedtls/md.h>
404#include <mbedtls/net_sockets.h> 433#include <mbedtls/net_sockets.h>
434#include <mbedtls/pk.h>
405#include <mbedtls/ssl.h> 435#include <mbedtls/ssl.h>
406#include <mbedtls/entropy.h>
407#include <mbedtls/ctr_drbg.h>
408#include <mbedtls/x509.h> 436#include <mbedtls/x509.h>
409#include <mbedtls/pk.h>
410#include <mbedtls/md.h>
411#include <mbedtls/debug.h>
412#include "mbedtls/error.h"
413#include "mbedtls/version.h"
414 437
415#include <sys/socket.h> 438#include <sys/socket.h>
416 439
417const char *DRBG_PERS = "mbed TLS vchat client"; 440const char *DRBG_PERS = "mbed TLS vchat client";
418#define MAX_SUITES 512 441#define MAX_SUITES 512
419typedef struct { 442typedef struct {
420 mbedtls_entropy_context _entropy; 443 mbedtls_entropy_context _entropy;
421 mbedtls_ctr_drbg_context _ctr_drbg; 444 mbedtls_ctr_drbg_context _ctr_drbg;
422 mbedtls_x509_crt _cacert; 445 mbedtls_x509_crt _cacert;
423 mbedtls_x509_crt _cert; 446 mbedtls_x509_crt _cert;
424 mbedtls_pk_context _key; 447 mbedtls_pk_context _key;
425 mbedtls_ssl_context _ssl; 448 mbedtls_ssl_context _ssl;
426 mbedtls_ssl_config _conf; 449 mbedtls_ssl_config _conf;
427 int ciphersuits[MAX_SUITES]; 450 int ciphersuits[MAX_SUITES];
428} mbedstate; 451} mbedstate;
429static mbedstate _mbedtls_state; 452static mbedstate _mbedtls_state;
430 453
431void vchat_tls_get_version_external() 454void vchat_tls_get_version_external() {
432{ 455 snprintf(tmpstr, sizeof(tmpstr), "%s", MBEDTLS_VERSION_STRING_FULL);
433 snprintf(tmpstr, sizeof(tmpstr), "%s", MBEDTLS_VERSION_STRING_FULL); 456 vchat_tls_version_external = strdup(tmpstr);
434 vchat_tls_version_external = strdup(tmpstr);
435} 457}
436 458
437static int static_tcp_recv(void *ctx, unsigned char *buf, size_t len ) { 459static int static_tcp_recv(void *ctx, unsigned char *buf, size_t len) {
438 return recv((int)(intptr_t)ctx, buf, len, 0); 460 return recv((int)(intptr_t)ctx, buf, len, 0);
439} 461}
440static int static_tcp_send(void *ctx, const unsigned char *buf, size_t len ) { 462static int static_tcp_send(void *ctx, const unsigned char *buf, size_t len) {
441 return send((int)(intptr_t)ctx, buf, len, 0); 463 return send((int)(intptr_t)ctx, buf, len, 0);
442} 464}
443static int map_openssl_suite(char *openssl_name); 465static int map_openssl_suite(char *openssl_name);
444void vc_init_x509store(vc_x509store_t *store) 466void vc_init_x509store(vc_x509store_t *store) {
445{ 467 static int sslinit;
446 static int sslinit; 468 if (!sslinit++) {
447 if (!sslinit++) { 469 mbedtls_entropy_init(&_mbedtls_state._entropy);
448 mbedtls_entropy_init(&_mbedtls_state._entropy); 470 mbedtls_ctr_drbg_init(&_mbedtls_state._ctr_drbg);
449 mbedtls_ctr_drbg_init(&_mbedtls_state._ctr_drbg); 471
450 472 mbedtls_ctr_drbg_seed(&_mbedtls_state._ctr_drbg, mbedtls_entropy_func,
451 mbedtls_ctr_drbg_seed(&_mbedtls_state._ctr_drbg, mbedtls_entropy_func, &_mbedtls_state._entropy, 473 &_mbedtls_state._entropy,
452 (const unsigned char *) DRBG_PERS, sizeof (DRBG_PERS)); 474 (const unsigned char *)DRBG_PERS, sizeof(DRBG_PERS));
453 } 475 }
454 memset(store, 0, sizeof(vc_x509store_t)); 476 memset(store, 0, sizeof(vc_x509store_t));
455 477
456 /* We want to make verifying the peer the default */ 478 /* We want to make verifying the peer the default */
457 store->flags |= VC_X509S_SSL_VERIFY_PEER; 479 store->flags |= VC_X509S_SSL_VERIFY_PEER;
458} 480}
459 481
460static void vc_tls_report_error(int error, char *message) { 482static void vc_tls_report_error(int error, char *message) {
461 size_t used = snprintf(tmpstr, sizeof(tmpstr), "Error: %s", message); 483 size_t used = snprintf(tmpstr, sizeof(tmpstr), "Error: %s", message);
462 mbedtls_strerror(error, tmpstr + used, sizeof(tmpstr) - used); 484 mbedtls_strerror(error, tmpstr + used, sizeof(tmpstr) - used);
463 writecf(FS_ERR, tmpstr); 485 writecf(FS_ERR, tmpstr);
464} 486}
465 487
466int vc_tls_connect( int serverfd, vc_x509store_t *vc_store ) 488int vc_tls_connect(int serverfd, vc_x509store_t *vc_store) {
467{ 489 /* Some aliases for shorter references */
468 /* Some aliases for shorter references */ 490 mbedstate *s = &_mbedtls_state;
469 mbedstate *s = &_mbedtls_state; 491 mbedtls_ssl_config *conf = &_mbedtls_state._conf;
470 mbedtls_ssl_config *conf = &_mbedtls_state._conf; 492 mbedtls_ssl_context *ssl = &_mbedtls_state._ssl;
471 mbedtls_ssl_context *ssl = &_mbedtls_state._ssl; 493 int ret, suitecount = 0;
472 int ret, suitecount = 0; 494 char fingerprint[128], *token;
473 char fingerprint[128], *token; 495 uint8_t digest[20];
474 uint8_t digest[20]; 496
475 497 mbedtls_x509_crt_init(&s->_cacert);
476 mbedtls_x509_crt_init(&s->_cacert); 498 mbedtls_x509_crt_init(&s->_cert);
477 mbedtls_x509_crt_init(&s->_cert); 499 mbedtls_pk_init(&s->_key);
478 mbedtls_pk_init(&s->_key); 500 mbedtls_ssl_init(ssl);
479 mbedtls_ssl_init(ssl); 501 mbedtls_ssl_config_init(conf);
480 mbedtls_ssl_config_init(conf); 502
481 503 writecf(FS_SERV, "[SOCKET CONNECTED ]");
482 writecf(FS_SERV, "[SOCKET CONNECTED ]"); 504 writecf(FS_SERV, "[UPGRADING TO TLS ]");
483 writecf(FS_SERV, "[UPGRADING TO TLS ]"); 505
484 506 if ((ret = mbedtls_ssl_config_defaults(conf, MBEDTLS_SSL_IS_CLIENT,
485 if ((ret = mbedtls_ssl_config_defaults(conf, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT)) != 0) { 507 MBEDTLS_SSL_TRANSPORT_STREAM,
486 vc_tls_report_error(ret, "Can not initialise tls parameters, mbedtls reports: "); 508 MBEDTLS_SSL_PRESET_DEFAULT)) != 0) {
487 return -1; 509 vc_tls_report_error(ret,
488 } 510 "Can not initialise tls parameters, mbedtls reports: ");
489 511 return -1;
490 /* TODO: Always verify peer */ 512 }
491 mbedtls_ssl_conf_authmode(conf, getintoption(CF_IGNSSL) ? MBEDTLS_SSL_VERIFY_OPTIONAL : MBEDTLS_SSL_VERIFY_REQUIRED); 513
492 mbedtls_ssl_conf_rng(conf, mbedtls_ctr_drbg_random, &s->_ctr_drbg); 514 /* TODO: Always verify peer */
493 515 mbedtls_ssl_conf_authmode(conf, getintoption(CF_IGNSSL)
494 char *ciphers = getstroption(CF_CIPHERSUITE); 516 ? MBEDTLS_SSL_VERIFY_OPTIONAL
495 if (!ciphers) 517 : MBEDTLS_SSL_VERIFY_REQUIRED);
496 ciphers = "ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA"; 518 mbedtls_ssl_conf_rng(conf, mbedtls_ctr_drbg_random, &s->_ctr_drbg);
497 ciphers = strdup(ciphers); 519
498 for (token = strtok(ciphers, ":"); token && suitecount < MAX_SUITES - 1; token = strtok(NULL, ":")) { 520 char *ciphers = getstroption(CF_CIPHERSUITE);
499 int suite = mbedtls_ssl_get_ciphersuite_id(token); 521 if (!ciphers)
500 if (!suite) 522 ciphers = "ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA";
501 suite = map_openssl_suite(token); 523 ciphers = strdup(ciphers);
502 if (suite) 524 for (token = strtok(ciphers, ":"); token && suitecount < MAX_SUITES - 1;
503 s->ciphersuits[suitecount++] = suite; 525 token = strtok(NULL, ":")) {
526 int suite = mbedtls_ssl_get_ciphersuite_id(token);
527 if (!suite)
528 suite = map_openssl_suite(token);
529 if (suite)
530 s->ciphersuits[suitecount++] = suite;
531 }
532 s->ciphersuits[suitecount++] = 0;
533 free(ciphers);
534
535 mbedtls_ssl_conf_ciphersuites(conf, s->ciphersuits);
536
537 if (vc_store->cafile) {
538 if ((ret = mbedtls_x509_crt_parse_file(&s->_cacert, vc_store->cafile)) !=
539 0) {
540 vc_tls_report_error(ret, "Can not load CA cert, mbedtls reports: ");
541 return -1;
504 } 542 }
505 s->ciphersuits[suitecount++] = 0; 543 mbedtls_ssl_conf_ca_chain(conf, &s->_cacert, NULL);
506 free(ciphers); 544 writecf(FS_SERV, "[CA CERT LOADED ]");
507 545 }
508 mbedtls_ssl_conf_ciphersuites(conf, s->ciphersuits); 546
509 547 if (vc_store->flags & VC_X509S_USE_CERTIFICATE) {
510 if (vc_store->cafile) { 548 char *password = NULL;
511 if ((ret = mbedtls_x509_crt_parse_file(&s->_cacert, vc_store->cafile)) != 0) { 549 char password_buf[1024];
512 vc_tls_report_error(ret, "Can not load CA cert, mbedtls reports: "); 550
513 return -1; 551 if ((ret = mbedtls_x509_crt_parse_file(&s->_cert, vc_store->certfile)) !=
514 } 552 0) {
515 mbedtls_ssl_conf_ca_chain(conf, &s->_cacert, NULL); 553 vc_tls_report_error(ret, "Can not load client cert, mbedtls reports: ");
516 writecf(FS_SERV, "[CA CERT LOADED ]"); 554 return -1;
517 } 555 }
556 writecf(FS_SERV, "[CLIENT CERT LOADED ]");
518 557
519 if (vc_store->flags & VC_X509S_USE_CERTIFICATE) { 558 while (1) {
520 char *password = NULL; 559 if ((ret = mbedtls_pk_parse_keyfile(&s->_key, vc_store->keyfile, password
521 char password_buf[1024];
522
523 if ((ret = mbedtls_x509_crt_parse_file(&s->_cert, vc_store->certfile)) != 0) {
524 vc_tls_report_error(ret, "Can not load client cert, mbedtls reports: ");
525 return -1;
526 }
527 writecf(FS_SERV, "[CLIENT CERT LOADED ]");
528
529 while (1) {
530 if ((ret = mbedtls_pk_parse_keyfile(&s->_key, vc_store->keyfile, password
531#if MBEDTLS_VERSION_MAJOR >= 3 560#if MBEDTLS_VERSION_MAJOR >= 3
532 , mbedtls_ctr_drbg_random, &s->_ctr_drbg 561 ,
562 mbedtls_ctr_drbg_random, &s->_ctr_drbg
533#endif 563#endif
534 )) == 0) 564 )) == 0)
535 break; 565 break;
536 if (ret != MBEDTLS_ERR_PK_PASSWORD_REQUIRED && ret != MBEDTLS_ERR_PK_PASSWORD_MISMATCH) { 566 if (ret != MBEDTLS_ERR_PK_PASSWORD_REQUIRED &&
537 vc_tls_report_error(ret, "Can not load client key, mbedtls reports: "); 567 ret != MBEDTLS_ERR_PK_PASSWORD_MISMATCH) {
538 return -1; 568 vc_tls_report_error(ret, "Can not load client key, mbedtls reports: ");
539 } 569 return -1;
540 if (ret == MBEDTLS_ERR_PK_PASSWORD_MISMATCH) 570 }
541 vc_tls_report_error(ret, "Wrong passphrase, mbedtls reports: "); 571 if (ret == MBEDTLS_ERR_PK_PASSWORD_MISMATCH)
542 vc_store->askpass_callback(password_buf, sizeof(password_buf), 0, NULL); 572 vc_tls_report_error(ret, "Wrong passphrase, mbedtls reports: ");
543 password = password_buf; 573 vc_store->askpass_callback(password_buf, sizeof(password_buf), 0, NULL);
544 } 574 password = password_buf;
545 memset_s(password_buf, sizeof(password_buf), 0, sizeof(password_buf)); 575 }
546 writecf(FS_SERV, "[CLIENT KEY LOADED ]"); 576 memset_s(password_buf, sizeof(password_buf), 0, sizeof(password_buf));
547 577 writecf(FS_SERV, "[CLIENT KEY LOADED ]");
548 578
549#if MBEDTLS_VERSION_MAJOR == 3 && MBEDTLS_VERSION_MINOR == 0 579#if MBEDTLS_VERSION_MAJOR == 3 && MBEDTLS_VERSION_MINOR == 0
550 mbedtls_pk_context *pubkey = &(s->_cert.MBEDTLS_PRIVATE(pk)); 580 mbedtls_pk_context *pubkey = &(s->_cert.MBEDTLS_PRIVATE(pk));
551#else 581#else
552 mbedtls_pk_context *pubkey = &(s->_cert.pk); 582 mbedtls_pk_context *pubkey = &(s->_cert.pk);
553#endif 583#endif
554 584
555 if ((ret = mbedtls_pk_check_pair(pubkey, &s->_key 585 if ((ret = mbedtls_pk_check_pair(pubkey, &s->_key
556#if MBEDTLS_VERSION_MAJOR >= 3 586#if MBEDTLS_VERSION_MAJOR >= 3
557 , mbedtls_ctr_drbg_random, &s->_ctr_drbg 587 ,
588 mbedtls_ctr_drbg_random, &s->_ctr_drbg
558#endif 589#endif
559 )) != 0) { 590 )) != 0) {
560 vc_tls_report_error(ret, "Cert and key mismatch, mbedtls reports: "); 591 vc_tls_report_error(ret, "Cert and key mismatch, mbedtls reports: ");
561 return 1; 592 return 1;
562 }
563
564 if ((ret = mbedtls_ssl_conf_own_cert(conf, &s->_cert, &s->_key)) != 0) {
565 vc_tls_report_error(ret, "Setting key and cert to tls session fails, mbedtls reports: ");
566 return -1;
567 }
568 } 593 }
569 594
570 /* Config constructed, pass to ssl */ 595 if ((ret = mbedtls_ssl_conf_own_cert(conf, &s->_cert, &s->_key)) != 0) {
571 /* Init ssl and config structs and configure ssl ctx */ 596 vc_tls_report_error(
572 if ((ret = mbedtls_ssl_setup(ssl, conf)) != 0) { 597 ret, "Setting key and cert to tls session fails, mbedtls reports: ");
573 vc_tls_report_error(ret, "Can not configure parameters on tls context, mbedtls reports: "); 598 return -1;
574 return -1; 599 }
600 }
601
602 /* Config constructed, pass to ssl */
603 /* Init ssl and config structs and configure ssl ctx */
604 if ((ret = mbedtls_ssl_setup(ssl, conf)) != 0) {
605 vc_tls_report_error(
606 ret, "Can not configure parameters on tls context, mbedtls reports: ");
607 return -1;
608 }
609 /* TODO: mbedtls_ssl_set_hostname(&ssl, SERVER_NAME) */
610
611 mbedtls_ssl_set_bio(ssl, (void *)(intptr_t)serverfd, static_tcp_send,
612 static_tcp_recv, NULL);
613
614 while ((ret = mbedtls_ssl_handshake(ssl)) != 0)
615 if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
616 vc_tls_report_error(ret, "TLS handshake failed, mbedtls reports: ");
617 return -1;
575 } 618 }
576 /* TODO: mbedtls_ssl_set_hostname(&ssl, SERVER_NAME) */
577
578 mbedtls_ssl_set_bio(ssl, (void*)(intptr_t)serverfd, static_tcp_send, static_tcp_recv, NULL );
579 619
580 while ((ret = mbedtls_ssl_handshake(ssl)) != 0) 620 writecf(FS_SERV, "[TLS HANDSHAKE DONE ]");
581 if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { 621 snprintf(tmpstr, TMPSTRSIZE, "[TLS CIPHER SUITE ] %s",
582 vc_tls_report_error(ret, "TLS handshake failed, mbedtls reports: "); 622 mbedtls_ssl_get_ciphersuite(ssl));
583 return -1; 623 writecf(FS_SERV, tmpstr);
584 }
585 624
586 writecf(FS_SERV,"[TLS HANDSHAKE DONE ]"); 625 const mbedtls_x509_crt *peer_cert = mbedtls_ssl_get_peer_cert(ssl);
587 snprintf(tmpstr, TMPSTRSIZE, "[TLS CIPHER SUITE ] %s", mbedtls_ssl_get_ciphersuite(ssl)); 626 mbedtls_x509_crt_info(tmpstr, sizeof(tmpstr), "[TLS PEER INFO ] ",
588 writecf(FS_SERV, tmpstr); 627 peer_cert);
589 628
590 const mbedtls_x509_crt* peer_cert = mbedtls_ssl_get_peer_cert(ssl); 629 for (token = strtok(tmpstr, "\n"); token; token = strtok(NULL, "\n"))
591 mbedtls_x509_crt_info(tmpstr, sizeof(tmpstr), "[TLS PEER INFO ] ", peer_cert); 630 writecf(FS_SERV, token);
592
593 for (token = strtok(tmpstr, "\n"); token; token = strtok(NULL, "\n"))
594 writecf(FS_SERV, token);
595 631
596#if MBEDTLS_VERSION_MAJOR == 3 && MBEDTLS_VERSION_MINOR == 0 632#if MBEDTLS_VERSION_MAJOR == 3 && MBEDTLS_VERSION_MINOR == 0
597 const uint8_t * const rawcert_buf = peer_cert->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(p); 633 const uint8_t *const rawcert_buf =
598 size_t rawcert_len = peer_cert->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(len); 634 peer_cert->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(p);
635 size_t rawcert_len = peer_cert->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(len);
599#else 636#else
600 const uint8_t * const rawcert_buf = peer_cert->raw.p; 637 const uint8_t *const rawcert_buf = peer_cert->raw.p;
601 size_t rawcert_len = peer_cert->raw.len; 638 size_t rawcert_len = peer_cert->raw.len;
602#endif 639#endif
603 const mbedtls_md_info_t *mdinfo = mbedtls_md_info_from_string("SHA1"); 640 const mbedtls_md_info_t *mdinfo = mbedtls_md_info_from_string("SHA1");
604 if (mdinfo) { 641 if (mdinfo) {
605 mbedtls_md(mdinfo, rawcert_buf, rawcert_len, digest); 642 mbedtls_md(mdinfo, rawcert_buf, rawcert_len, digest);
606 643
607 char *fp = fingerprint; 644 char *fp = fingerprint;
608 for (int j=0; j<mbedtls_md_get_size(mdinfo); j++) 645 for (int j = 0; j < mbedtls_md_get_size(mdinfo); j++)
609 fp += sprintf(fp, "%02X:", digest[j]); 646 fp += sprintf(fp, "%02X:", digest[j]);
610 assert ( fp > fingerprint ); 647 assert(fp > fingerprint);
611 fp[-1] = 0; 648 fp[-1] = 0;
612 snprintf(tmpstr, TMPSTRSIZE, "[TLS FINGERPRINT ] %s (from server)", fingerprint); 649 snprintf(tmpstr, TMPSTRSIZE, "[TLS FINGERPRINT ] %s (from server)",
613 writecf(FS_SERV, tmpstr); 650 fingerprint);
614 651 writecf(FS_SERV, tmpstr);
615 if (getintoption(CF_PINFINGER) && verify_or_store_fingerprint(fingerprint))
616 return 1;
617 } else {
618 writecf(FS_ERR, "Warning: Unable to load SHA-1 md");
619 if (getintoption(CF_PINFINGER)) {
620 writecf(FS_ERR, "ERROR: Can not compute fingerprint, but pinning check is required");
621 return 1;
622 }
623 }
624 652
625 ret = mbedtls_ssl_get_verify_result(ssl); 653 if (getintoption(CF_PINFINGER) && verify_or_store_fingerprint(fingerprint))
626 switch (ret) { 654 return 1;
627 case 0: 655 } else {
628 writecf(FS_SERV, "[TLS HANDSHAKE OK ]"); 656 writecf(FS_ERR, "Warning: Unable to load SHA-1 md");
629 break; 657 if (getintoption(CF_PINFINGER)) {
630 case -1: 658 writecf(
631 writecf(FS_ERR, "Error: TLS verify for an unknown reason"); 659 FS_ERR,
632 return -1; 660 "ERROR: Can not compute fingerprint, but pinning check is required");
633 case MBEDTLS_X509_BADCERT_SKIP_VERIFY: 661 return 1;
634 case MBEDTLS_X509_BADCERT_NOT_TRUSTED:
635 if (getintoption(CF_IGNSSL) || !getintoption(CF_VERIFYSSL))
636 return 0;
637 vc_tls_report_error(ret, "TLS verify failed, mbedtls reports: ");
638 return -1;
639 default:
640 vc_tls_report_error(ret, "TLS verify failed, mbedtls reports: ");
641 return -1;
642 } 662 }
643 663 }
644 return 0; 664
665 ret = mbedtls_ssl_get_verify_result(ssl);
666 switch (ret) {
667 case 0:
668 writecf(FS_SERV, "[TLS HANDSHAKE OK ]");
669 break;
670 case -1:
671 writecf(FS_ERR, "Error: TLS verify for an unknown reason");
672 return -1;
673 case MBEDTLS_X509_BADCERT_SKIP_VERIFY:
674 case MBEDTLS_X509_BADCERT_NOT_TRUSTED:
675 if (getintoption(CF_IGNSSL) || !getintoption(CF_VERIFYSSL))
676 return 0;
677 vc_tls_report_error(ret, "TLS verify failed, mbedtls reports: ");
678 return -1;
679 default:
680 vc_tls_report_error(ret, "TLS verify failed, mbedtls reports: ");
681 return -1;
682 }
683
684 return 0;
645} 685}
646 686
647ssize_t vc_tls_sendmessage(const void *buf, size_t size) { 687ssize_t vc_tls_sendmessage(const void *buf, size_t size) {
648 return mbedtls_ssl_write( &_mbedtls_state._ssl, buf, size); 688 return mbedtls_ssl_write(&_mbedtls_state._ssl, buf, size);
649} 689}
650 690
651ssize_t vc_tls_receivemessage(void *buf, size_t size) { 691ssize_t vc_tls_receivemessage(void *buf, size_t size) {
652 ssize_t received = (ssize_t)mbedtls_ssl_read (&_mbedtls_state._ssl, buf, size); 692 ssize_t received = (ssize_t)mbedtls_ssl_read(&_mbedtls_state._ssl, buf, size);
653 switch (received) { 693 switch (received) {
654 case MBEDTLS_ERR_SSL_WANT_READ: 694 case MBEDTLS_ERR_SSL_WANT_READ:
655 case MBEDTLS_ERR_SSL_WANT_WRITE: 695 case MBEDTLS_ERR_SSL_WANT_WRITE:
656 return -2; 696 return -2;
657 case MBEDTLS_ERR_SSL_CONN_EOF: 697 case MBEDTLS_ERR_SSL_CONN_EOF:
658 case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY: 698 case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY:
659 case 0: 699 case 0:
660 return 0; 700 return 0;
661 default: 701 default:
662 if (received > 0) 702 if (received > 0)
663 return received; 703 return received;
664 return -1; 704 return -1;
665 } 705 }
666} 706}
667 707
668void vc_tls_cleanup() { 708void vc_tls_cleanup() {
669 mbedtls_x509_crt_free(&_mbedtls_state._cacert); 709 mbedtls_x509_crt_free(&_mbedtls_state._cacert);
670 mbedtls_x509_crt_free(&_mbedtls_state._cert); 710 mbedtls_x509_crt_free(&_mbedtls_state._cert);
671 mbedtls_pk_free(&_mbedtls_state._key); 711 mbedtls_pk_free(&_mbedtls_state._key);
672 mbedtls_ssl_free(&_mbedtls_state._ssl ); 712 mbedtls_ssl_free(&_mbedtls_state._ssl);
673 mbedtls_ssl_config_free(&_mbedtls_state._conf ); 713 mbedtls_ssl_config_free(&_mbedtls_state._conf);
674 mbedtls_ctr_drbg_free(&_mbedtls_state._ctr_drbg ); 714 mbedtls_ctr_drbg_free(&_mbedtls_state._ctr_drbg);
675} 715}
676 716
677/* Taken from https://testssl.sh/openssl-iana.mapping.html */ 717/* Taken from https://testssl.sh/openssl-iana.mapping.html */
@@ -924,12 +964,12 @@ NULL
924}; 964};
925// fprintf(stderr, "SUCCESS: %s => %s => %d\n\n", xlate_openssl[i], xlate_openssl[i+1], mbedtls_ssl_get_ciphersuite_id(xlate_openssl[i+1])); 965// fprintf(stderr, "SUCCESS: %s => %s => %d\n\n", xlate_openssl[i], xlate_openssl[i+1], mbedtls_ssl_get_ciphersuite_id(xlate_openssl[i+1]));
926static int map_openssl_suite(char *openssl_name) { 966static int map_openssl_suite(char *openssl_name) {
927 int i; 967 int i;
928 for (i=0; xlate_openssl[i]; i+=2) { 968 for (i = 0; xlate_openssl[i]; i += 2) {
929 if (!strcmp(xlate_openssl[i], openssl_name)) 969 if (!strcmp(xlate_openssl[i], openssl_name))
930 return mbedtls_ssl_get_ciphersuite_id(xlate_openssl[i+1]); 970 return mbedtls_ssl_get_ciphersuite_id(xlate_openssl[i + 1]);
931 } 971 }
932 return 0; 972 return 0;
933} 973}
934 974
935#endif 975#endif
diff --git a/vchat-tls.h b/vchat-tls.h
index 440d08e..17d2687 100755
--- a/vchat-tls.h
+++ b/vchat-tls.h
@@ -4,13 +4,13 @@
4 4
5typedef int (*vc_askpass_cb_t)(char *, int, int, void *); 5typedef int (*vc_askpass_cb_t)(char *, int, int, void *);
6struct vc_x509store_t { 6struct vc_x509store_t {
7 char *cafile; 7 char *cafile;
8 char *capath; 8 char *capath;
9 char *crlfile; 9 char *crlfile;
10 vc_askpass_cb_t askpass_callback; 10 vc_askpass_cb_t askpass_callback;
11 char *certfile; 11 char *certfile;
12 char *keyfile; 12 char *keyfile;
13 int flags; 13 int flags;
14}; 14};
15typedef struct vc_x509store_t vc_x509store_t; 15typedef struct vc_x509store_t vc_x509store_t;
16 16
@@ -25,7 +25,7 @@ void vc_x509store_setcapath(vc_x509store_t *, char *);
25void vc_x509store_setcrlfile(vc_x509store_t *, char *); 25void vc_x509store_setcrlfile(vc_x509store_t *, char *);
26void vc_cleanup_x509store(vc_x509store_t *s); 26void vc_cleanup_x509store(vc_x509store_t *s);
27 27
28int vc_tls_connect(int serverfd, vc_x509store_t * ); 28int vc_tls_connect(int serverfd, vc_x509store_t *);
29ssize_t vc_tls_sendmessage(const void *buf, size_t size); 29ssize_t vc_tls_sendmessage(const void *buf, size_t size);
30ssize_t vc_tls_receivemessage(void *buf, size_t size); 30ssize_t vc_tls_receivemessage(void *buf, size_t size);
31void vc_tls_cleanup(); 31void vc_tls_cleanup();
diff --git a/vchat-ui.c b/vchat-ui.c
index 62ef372..bb33287 100755
--- a/vchat-ui.c
+++ b/vchat-ui.c
@@ -10,116 +10,123 @@
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#inc