diff options
| -rw-r--r--[-rwxr-xr-x] | Makefile | 42 | ||||
| -rw-r--r--[-rwxr-xr-x] | README | 0 | ||||
| -rw-r--r--[-rwxr-xr-x] | TODO | 0 | ||||
| -rw-r--r--[-rwxr-xr-x] | debian/changelog | 0 | ||||
| -rw-r--r--[-rwxr-xr-x] | debian/control | 0 | ||||
| -rw-r--r--[-rwxr-xr-x] | debian/copyright | 0 | ||||
| -rw-r--r--[-rwxr-xr-x] | debian/dirs | 0 | ||||
| -rw-r--r--[-rwxr-xr-x] | debian/docs | 0 | ||||
| -rw-r--r--[-rwxr-xr-x] | debian/rules | 0 | ||||
| -rw-r--r--[-rwxr-xr-x] | filters | 0 | ||||
| -rw-r--r--[-rwxr-xr-x] | sample-count.fmt | 0 | ||||
| -rw-r--r--[-rwxr-xr-x] | sample-devlog.fmt | 0 | ||||
| -rw-r--r--[-rwxr-xr-x] | sample-erdgeist.fmt | 0 | ||||
| -rw-r--r--[-rwxr-xr-x] | sample-mAsq.fmt | 0 | ||||
| -rw-r--r--[-rwxr-xr-x] | sample-oldstyle.fmt | 0 | ||||
| -rw-r--r--[-rwxr-xr-x] | sample-xzivi.fmt | 0 | ||||
| -rw-r--r--[-rwxr-xr-x] | vchat-client.c | 624 | ||||
| -rw-r--r--[-rwxr-xr-x] | vchat-client.sgml | 2 | ||||
| -rw-r--r--[-rwxr-xr-x] | vchat-commands.c | 523 | ||||
| -rw-r--r--[-rwxr-xr-x] | vchat-config.h | 5 | ||||
| -rw-r--r-- | vchat-connection.c | 350 | ||||
| -rw-r--r-- | vchat-connection.h | 11 | ||||
| -rw-r--r--[-rwxr-xr-x] | vchat-help.h | 0 | ||||
| -rw-r--r--[-rwxr-xr-x] | vchat-howto | 0 | ||||
| -rwxr-xr-x | vchat-keygen | 7 | ||||
| -rw-r--r--[-rwxr-xr-x] | vchat-messages.h | 0 | ||||
| -rw-r--r--[-rwxr-xr-x] | vchat-protocol.c | 799 | ||||
| -rwxr-xr-x | vchat-ssl.c | 475 | ||||
| -rwxr-xr-x | vchat-ssl.h | 23 | ||||
| -rw-r--r-- | vchat-tls.c | 970 | ||||
| -rw-r--r-- | vchat-tls.h | 58 | ||||
| -rw-r--r--[-rwxr-xr-x] | vchat-ui.c | 1984 | ||||
| -rw-r--r--[-rwxr-xr-x] | vchat-user.c | 399 | ||||
| -rw-r--r--[-rwxr-xr-x] | vchat.h | 236 | ||||
| -rw-r--r--[-rwxr-xr-x] | vchatrc.ex | 0 |
35 files changed, 3702 insertions, 2806 deletions
| @@ -6,18 +6,42 @@ | |||
| 6 | # configuration # | 6 | # configuration # |
| 7 | ############################################## | 7 | ############################################## |
| 8 | 8 | ||
| 9 | CFLAGS = -Wall -Os | 9 | OBJS = vchat-client.o vchat-ui.o vchat-protocol.o vchat-user.o vchat-commands.o vchat-tls.o vchat-connection.o |
| 10 | #CFLAGS = -Wall -g -ggdb | 10 | |
| 11 | # On FreeBSD you might want to link -ncursesw | ||
| 12 | LIBS = -lncurses | ||
| 13 | #LIBS = -lncursesw | ||
| 14 | |||
| 15 | LIBS += -lreadline | ||
| 16 | |||
| 17 | CFLAGS += -Wall -Os | ||
| 18 | CFLAGS += -I/usr/local/include | ||
| 19 | LDFLAGS += -L/usr/local/lib | ||
| 11 | 20 | ||
| 12 | ## use this line when you've got an readline before 4.(x|2) | 21 | ## use this line when you've got an readline before 4.(x|2) |
| 13 | #CFLAGS += -DOLDREADLINE | 22 | #CFLAGS += -DOLDREADLINE |
| 14 | 23 | ||
| 24 | # Alternatively, you can just build with make OLDREADLINE=-DOLDREADLINE | ||
| 25 | # if you can't modify this Makefile | ||
| 15 | CFLAGS += $(OLDREADLINE) | 26 | CFLAGS += $(OLDREADLINE) |
| 16 | 27 | ||
| 28 | ##### Enable this for enabling the OpenSSL library | ||
| 29 | CFLAGS += -DTLS_LIB_OPENSSL | ||
| 30 | LIBS += -lssl -lcrypto | ||
| 31 | |||
| 32 | ##### Enable this for enabling the mbedTLS library | ||
| 33 | #CFLAGS += -DTLS_LIB_MBEDTLS | ||
| 34 | #LIBS += -lmbedx509 -lmbedtls -lmbedcrypto | ||
| 35 | |||
| 17 | ## you might need one or more of these: | 36 | ## you might need one or more of these: |
| 37 | #CFLAGS+= -Wextra -Wall -g -ggdb | ||
| 38 | #CFLAGS+= -arch x86_64 -Wno-deprecated-declarations | ||
| 39 | #CFLAGS+= -arch i386 -Wno-deprecated-declarations | ||
| 18 | #CFLAGS += -I/usr/local/ssl/include -L/usr/local/ssl/lib | 40 | #CFLAGS += -I/usr/local/ssl/include -L/usr/local/ssl/lib |
| 19 | #CFLAGS += -I/usr/local/include -L/usr/local/lib | ||
| 20 | #CFLAGS += -I/usr/pkg/include -L/usr/pkg/lib | 41 | #CFLAGS += -I/usr/pkg/include -L/usr/pkg/lib |
| 42 | #LDFLAGS += -L"/usr/local/opt/openssl@1.1/lib" | ||
| 43 | #CFLAGS += -I../readline-6.3 | ||
| 44 | #LIBS += ../readline-6.3/libreadline.a | ||
| 21 | 45 | ||
| 22 | ## enable dietlibc | 46 | ## enable dietlibc |
| 23 | #CC = diet cc | 47 | #CC = diet cc |
| @@ -29,9 +53,6 @@ CFLAGS += $(OLDREADLINE) | |||
| 29 | ## the install prefix best is /usr/local | 53 | ## the install prefix best is /usr/local |
| 30 | PREFIX=/usr/local | 54 | PREFIX=/usr/local |
| 31 | 55 | ||
| 32 | LIBS = -lreadline -lncursesw -lssl -lcrypto | ||
| 33 | OBJS = vchat-client.o vchat-ui.o vchat-protocol.o vchat-user.o vchat-commands.o vchat-ssl.o | ||
| 34 | |||
| 35 | 56 | ||
| 36 | ############################################## | 57 | ############################################## |
| 37 | # general targets # | 58 | # general targets # |
| @@ -66,7 +87,7 @@ clean: | |||
| 66 | ############################################## | 87 | ############################################## |
| 67 | 88 | ||
| 68 | vchat-client: $(OBJS) | 89 | vchat-client: $(OBJS) |
| 69 | $(CC) $(CFLAGS) -o vchat-client $(OBJS) $(LIBS) | 90 | $(CC) $(CFLAGS) -o vchat-client $(OBJS) $(LIBS) $(LDFLAGS) |
| 70 | 91 | ||
| 71 | vchat-client.o: vchat-client.c vchat-config.h Makefile | 92 | vchat-client.o: vchat-client.c vchat-config.h Makefile |
| 72 | $(CC) $(CFLAGS) -o vchat-client.o -c vchat-client.c | 93 | $(CC) $(CFLAGS) -o vchat-client.o -c vchat-client.c |
| @@ -83,8 +104,11 @@ vchat-user.o: vchat-user.c vchat.h | |||
| 83 | vchat-commands.o: vchat-commands.c vchat.h vchat-config.h | 104 | vchat-commands.o: vchat-commands.c vchat.h vchat-config.h |
| 84 | $(CC) $(CFLAGS) -o vchat-commands.o -c vchat-commands.c | 105 | $(CC) $(CFLAGS) -o vchat-commands.o -c vchat-commands.c |
| 85 | 106 | ||
| 86 | vchat-ssl.o: vchat-ssl.c vchat-ssl.h | 107 | vchat-tls.o: vchat-tls.c vchat-tls.h |
| 87 | $(CC) $(CFLAGS) -o vchat-ssl.o -c vchat-ssl.c | 108 | $(CC) $(CFLAGS) -o vchat-tls.o -c vchat-tls.c |
| 109 | |||
| 110 | vchat-connection.o: vchat-connection.c vchat-connection.h | ||
| 111 | $(CC) $(CFLAGS) -o vchat-connection.o -c vchat-connection.c | ||
| 88 | 112 | ||
| 89 | #vchat-client.1: vchat-client.sgml | 113 | #vchat-client.1: vchat-client.sgml |
| 90 | # docbook2man vchat-client.sgml | 114 | # docbook2man vchat-client.sgml |
diff --git a/debian/changelog b/debian/changelog index eb2b185..eb2b185 100755..100644 --- a/debian/changelog +++ b/debian/changelog | |||
diff --git a/debian/control b/debian/control index 3ba3fcd..3ba3fcd 100755..100644 --- a/debian/control +++ b/debian/control | |||
diff --git a/debian/copyright b/debian/copyright index e066d1d..e066d1d 100755..100644 --- a/debian/copyright +++ b/debian/copyright | |||
diff --git a/debian/dirs b/debian/dirs index e772481..e772481 100755..100644 --- a/debian/dirs +++ b/debian/dirs | |||
diff --git a/debian/docs b/debian/docs index 724e084..724e084 100755..100644 --- a/debian/docs +++ b/debian/docs | |||
diff --git a/debian/rules b/debian/rules index 778533f..778533f 100755..100644 --- a/debian/rules +++ b/debian/rules | |||
diff --git a/sample-count.fmt b/sample-count.fmt index bab9751..bab9751 100755..100644 --- a/sample-count.fmt +++ b/sample-count.fmt | |||
diff --git a/sample-devlog.fmt b/sample-devlog.fmt index ac30551..ac30551 100755..100644 --- a/sample-devlog.fmt +++ b/sample-devlog.fmt | |||
diff --git a/sample-erdgeist.fmt b/sample-erdgeist.fmt index 7545d3d..7545d3d 100755..100644 --- a/sample-erdgeist.fmt +++ b/sample-erdgeist.fmt | |||
diff --git a/sample-mAsq.fmt b/sample-mAsq.fmt index af001d7..af001d7 100755..100644 --- a/sample-mAsq.fmt +++ b/sample-mAsq.fmt | |||
diff --git a/sample-oldstyle.fmt b/sample-oldstyle.fmt index d0d8f17..d0d8f17 100755..100644 --- a/sample-oldstyle.fmt +++ b/sample-oldstyle.fmt | |||
diff --git a/sample-xzivi.fmt b/sample-xzivi.fmt index dbbf2b4..dbbf2b4 100755..100644 --- a/sample-xzivi.fmt +++ b/sample-xzivi.fmt | |||
diff --git a/vchat-client.c b/vchat-client.c index 0e480be..92d1905 100755..100644 --- a/vchat-client.c +++ b/vchat-client.c | |||
| @@ -15,24 +15,27 @@ | |||
| 15 | */ | 15 | */ |
| 16 | 16 | ||
| 17 | /* general includes */ | 17 | /* general includes */ |
| 18 | #include <sys/types.h> | 18 | #include <errno.h> |
| 19 | #include <sys/time.h> | 19 | #include <locale.h> |
| 20 | #include <signal.h> | ||
| 20 | #include <stdint.h> | 21 | #include <stdint.h> |
| 21 | #include <time.h> | ||
| 22 | #include <string.h> | ||
| 23 | #include <unistd.h> | ||
| 24 | #include <stdio.h> | 22 | #include <stdio.h> |
| 25 | #include <stdlib.h> | 23 | #include <stdlib.h> |
| 26 | #include <errno.h> | 24 | #include <string.h> |
| 27 | #include <signal.h> | 25 | #include <sys/time.h> |
| 26 | #include <sys/types.h> | ||
| 27 | #include <time.h> | ||
| 28 | #include <unistd.h> | ||
| 29 | |||
| 28 | #include <readline/readline.h> | 30 | #include <readline/readline.h> |
| 29 | #include <locale.h> | ||
| 30 | 31 | ||
| 31 | #include "vchat.h" | 32 | #include "vchat-connection.h" |
| 32 | #include "vchat-user.h" | 33 | #include "vchat-user.h" |
| 34 | #include "vchat.h" | ||
| 33 | 35 | ||
| 34 | /* version of this module */ | 36 | /* version of this module */ |
| 35 | const char *vchat_cl_version = "vchat-client.c $Id$"; | 37 | const char *vchat_cl_version = |
| 38 | "vchat-client.c $Id$"; | ||
| 36 | 39 | ||
| 37 | /* externally used variables */ | 40 | /* externally used variables */ |
| 38 | /* we're logged in */ | 41 | /* we're logged in */ |
| @@ -43,7 +46,6 @@ int status = 1; | |||
| 43 | int ownquit = 0; | 46 | int ownquit = 0; |
| 44 | /* we set this, we DONT want to quit */ | 47 | /* we set this, we DONT want to quit */ |
| 45 | int wantreconnect = 0; | 48 | int wantreconnect = 0; |
| 46 | unsigned int want_tcp_keepalive = 0; | ||
| 47 | 49 | ||
| 48 | static int reconnect_delay = 6; | 50 | static int reconnect_delay = 6; |
| 49 | static time_t reconnect_time = 0; | 51 | static time_t reconnect_time = 0; |
| @@ -51,149 +53,152 @@ static time_t reconnect_time = 0; | |||
| 51 | /* error string to show after exit */ | 53 | /* error string to show after exit */ |
| 52 | char errstr[ERRSTRSIZE] = "\0"; | 54 | char errstr[ERRSTRSIZE] = "\0"; |
| 53 | 55 | ||
| 54 | /* locally global variables */ | ||
| 55 | /* our list of filedescriptors */ | ||
| 56 | static fd_set masterfds; | ||
| 57 | |||
| 58 | /* declaration of configuration array */ | 56 | /* declaration of configuration array */ |
| 59 | #include "vchat-config.h" | 57 | #include "vchat-config.h" |
| 60 | 58 | ||
| 61 | /* servers filedescriptor from vchat-protocol.c */ | 59 | void setnoption(const char *, char *); |
| 62 | extern int serverfd; | ||
| 63 | |||
| 64 | void setnoption (char *, char *); | ||
| 65 | 60 | ||
| 66 | static void parsecfg(char *line) { | 61 | static void parsecfg(char *line) { |
| 67 | int bytes; | 62 | int bytes; |
| 68 | char *param=line; | 63 | char *param = line; |
| 69 | char *value=NULL; | 64 | char *value = NULL; |
| 70 | 65 | ||
| 71 | /* handle quotes value is empty, so we can use it */ | 66 | /* handle quotes value is empty, so we can use it */ |
| 72 | value = strchr(line,'#'); | 67 | value = strchr(line, '#'); |
| 73 | if (value) { /* the line contains a cute little quote */ | 68 | if (value) { /* the line contains a cute little quote */ |
| 74 | value[0]='\0'; /* ignore the rest of the line */ | 69 | value[0] = '\0'; /* ignore the rest of the line */ |
| 75 | } | 70 | } |
| 76 | 71 | ||
| 77 | /* now split the line into two parts */ | 72 | /* now split the line into two parts */ |
| 78 | value = strchr(line,'='); | 73 | value = strchr(line, '='); |
| 79 | if (!value) return; /* exit if strchr fails */ | 74 | if (!value) |
| 80 | value[0]='\0'; | 75 | return; /* exit if strchr fails */ |
| 76 | value[0] = '\0'; | ||
| 81 | value++; | 77 | value++; |
| 82 | 78 | ||
| 83 | /* "trim" values */ | 79 | /* "trim" values */ |
| 84 | while ((value[0] == ' ')||(value[0] == '\t')) | 80 | while ((value[0] == ' ') || (value[0] == '\t')) |
| 85 | value++; | 81 | value++; |
| 86 | bytes = strlen(value); | 82 | bytes = strlen(value); |
| 87 | while ((value[bytes-1] == ' ')||(value[bytes-1] == '\t')) { | 83 | while ((value[bytes - 1] == ' ') || (value[bytes - 1] == '\t')) { |
| 88 | value[bytes-1] = '\0'; | 84 | value[bytes - 1] = '\0'; |
| 89 | bytes=strlen(value); | 85 | bytes = strlen(value); |
| 90 | } | 86 | } |
| 91 | /* bytes should be strlen(value) */ | 87 | /* bytes should be strlen(value) */ |
| 92 | if ( value[bytes-1] == '"' ) value[bytes-1] = '\0'; | 88 | if (value[bytes - 1] == '"') |
| 93 | if ( value[0] == '"' ) value++; | 89 | value[bytes - 1] = '\0'; |
| 90 | if (value[0] == '"') | ||
| 91 | value++; | ||
| 94 | 92 | ||
| 95 | /* "trim" param */ | 93 | /* "trim" param */ |
| 96 | while ((param[0] == ' ')||(param[0] == '\t')) | 94 | while ((param[0] == ' ') || (param[0] == '\t')) |
| 97 | param++; | 95 | param++; |
| 98 | bytes = strlen(param); | 96 | bytes = strlen(param); |
| 99 | while ((param[bytes-1] == ' ')||(param[bytes-1] == '\t')) { | 97 | while ((param[bytes - 1] == ' ') || (param[bytes - 1] == '\t')) { |
| 100 | param[bytes-1] = '\0'; | 98 | param[bytes - 1] = '\0'; |
| 101 | bytes=strlen(param); | 99 | bytes = strlen(param); |
| 102 | } | 100 | } |
| 103 | /* bytes should be strlen(param) */ | 101 | /* bytes should be strlen(param) */ |
| 104 | if ( param[bytes-1] == '\"' ) param[bytes-1] = '\0'; | 102 | if (param[bytes - 1] == '\"') |
| 105 | if ( param[0] == '\"' ) param++; | 103 | param[bytes - 1] = '\0'; |
| 104 | if (param[0] == '\"') | ||
| 105 | param++; | ||
| 106 | 106 | ||
| 107 | if ((!param)||(!value)) return; /* failsave */ | 107 | if ((!param) || (!value)) |
| 108 | return; /* failsave */ | ||
| 108 | 109 | ||
| 109 | //fprintf(stderr,"\"%s\" -> \"%s\"\n",param,value); | 110 | // fprintf(stderr,"\"%s\" -> \"%s\"\n",param,value); |
| 110 | setnoption(param,value); | 111 | setnoption(param, value); |
| 111 | } | 112 | } |
| 112 | 113 | ||
| 113 | static void parseformats(char *line) { | 114 | static void parseformats(char *line) { |
| 114 | int i; | 115 | int i; |
| 115 | char *tmp = NULL; | 116 | char *tmp = NULL; |
| 116 | 117 | ||
| 117 | /* read a format line from file, syntax is | 118 | /* read a format line from file, syntax is |
| 118 | FS_XXX = "formatstring" | 119 | FS_XXX = "formatstring" |
| 119 | */ | 120 | */ |
| 120 | 121 | ||
| 121 | while( *line == ' ') line++; | 122 | while (*line == ' ') |
| 122 | 123 | line++; | |
| 123 | if( strlen( line ) > TMPSTRSIZE ) return; | 124 | |
| 124 | 125 | if (strlen(line) > TMPSTRSIZE) | |
| 125 | if( *line != '#') /* allow to comment out the line */ | 126 | return; |
| 126 | for (i = 0; formatstrings[i].formatstr; i++) | 127 | |
| 127 | if (!strncasecmp(formatstrings[i].idstring, line, strlen( formatstrings[i].idstring) )) | 128 | if (*line != '#') /* allow to comment out the line */ |
| 128 | { | 129 | for (i = 0; formatstrings[i].formatstr; i++) |
| 129 | char *tail = line + strlen( formatstrings[i].idstring); | 130 | if (!strncasecmp(formatstrings[i].idstring, line, |
| 130 | while( *tail==' ' || *tail=='\t') tail++; /* and skip whitespaces */ | 131 | strlen(formatstrings[i].idstring))) { |
| 131 | 132 | char *tail = line + strlen(formatstrings[i].idstring); | |
| 132 | if( *tail++ == '=' ) | 133 | while (*tail == ' ' || *tail == '\t') |
| 133 | { | 134 | tail++; /* and skip whitespaces */ |
| 134 | while( *tail==' ' || *tail=='\t') tail++; | 135 | |
| 135 | if( *(tail++)=='\"' ) | 136 | if (*tail++ == '=') { |
| 136 | { | 137 | while (*tail == ' ' || *tail == '\t') |
| 137 | int j, k = 0, stringends = 0, backslash=0; | 138 | tail++; |
| 138 | for ( j = 0; tail[j] && !stringends; j++) | 139 | if (*(tail++) == '\"') { |
| 139 | { | 140 | int j, k = 0, stringends = 0, backslash = 0; |
| 140 | switch( tail[j] ) { | 141 | for (j = 0; tail[j] && !stringends; j++) { |
| 141 | case '^': | 142 | switch (tail[j]) { |
| 142 | if ( tail[j+1] != '^' ) | 143 | case '^': |
| 143 | tmpstr[k++] = 1; | 144 | if (tail[j + 1] != '^') |
| 144 | break; | 145 | tmpstr[k++] = 1; |
| 145 | case '\\': | 146 | break; |
| 146 | backslash=1-backslash; | 147 | case '\\': |
| 147 | tmpstr[k++] = '\\'; | 148 | backslash = 1 - backslash; |
| 148 | break; | 149 | tmpstr[k++] = '\\'; |
| 149 | case '\"': | 150 | break; |
| 150 | if (backslash) k--; else stringends = 1; | 151 | case '\"': |
| 151 | default: | 152 | if (backslash) |
| 152 | tmpstr[k++] = tail[j]; | 153 | k--; |
| 153 | backslash = 0; | 154 | else |
| 154 | } | 155 | stringends = 1; |
| 155 | } | 156 | default: |
| 156 | 157 | tmpstr[k++] = tail[j]; | |
| 157 | if ( stringends && ( (tmp = (char *)malloc( 1 + j )) != NULL ) ) | 158 | backslash = 0; |
| 158 | { | ||
| 159 | memcpy( tmp, tmpstr, k); | ||
| 160 | tmp[k-1]=0; | ||
| 161 | formatstrings[i].formatstr = tmp; | ||
| 162 | } | ||
| 163 | } | ||
| 164 | } | 159 | } |
| 165 | } | 160 | } |
| 166 | 161 | ||
| 162 | if (stringends && ((tmp = (char *)malloc(1 + j)) != NULL)) { | ||
| 163 | memcpy(tmp, tmpstr, k); | ||
| 164 | tmp[k - 1] = 0; | ||
| 165 | formatstrings[i].formatstr = tmp; | ||
| 166 | } | ||
| 167 | } | ||
| 168 | } | ||
| 169 | } | ||
| 167 | } | 170 | } |
| 168 | 171 | ||
| 169 | /* UNUSED uncomment if needed | 172 | /* UNUSED uncomment if needed |
| 170 | static void parseknownhosts(char *line) { | 173 | static void parseknownhosts(char *line) { |
| 171 | } | 174 | } |
| 172 | */ | 175 | */ |
| 173 | 176 | ||
| 174 | /* load config file */ | 177 | /* load config file */ |
| 175 | void | 178 | void loadcfg(char *file, int complain, void (*lineparser)(char *)) { |
| 176 | loadcfg (char *file,int complain, void (*lineparser) (char *)) | ||
| 177 | { | ||
| 178 | FILE *fh; | 179 | FILE *fh; |
| 179 | #define BUFSIZE 4096 | 180 | #define BUFSIZE 4096 |
| 180 | char buf[BUFSIZE]; /* data buffer */ | 181 | char buf[BUFSIZE]; /* data buffer */ |
| 181 | char *tildex = NULL, *t; | 182 | char *tildex = NULL, *t; |
| 182 | 183 | ||
| 183 | /* Check and expand filename then open file */ | 184 | /* Check and expand filename then open file */ |
| 184 | if (!file) return; | 185 | if (!file) |
| 185 | tildex = tilde_expand( file ); | 186 | return; |
| 186 | if (!tildex) return; | 187 | tildex = tilde_expand(file); |
| 187 | fh = fopen( tildex, "r" ); | 188 | if (!tildex) |
| 188 | free( tildex ); | 189 | return; |
| 190 | fh = fopen(tildex, "r"); | ||
| 191 | free(tildex); | ||
| 189 | 192 | ||
| 190 | if (!fh) { | 193 | if (!fh) { |
| 191 | if( complain ) snprintf (errstr, TMPSTRSIZE, "Can't open config-file \"%s\": %s.", file, strerror(errno)); | 194 | if (complain) |
| 195 | snprintf(errstr, TMPSTRSIZE, "Can't open config-file \"%s\": %s.", file, | ||
| 196 | strerror(errno)); | ||
| 192 | return; | 197 | return; |
| 193 | } | 198 | } |
| 194 | 199 | ||
| 195 | while ( fgets( buf, sizeof(buf), fh ) ) { | 200 | while (fgets(buf, sizeof(buf), fh)) { |
| 196 | if( ( t = strchr( buf, '\n' ) ) ) | 201 | if ((t = strchr(buf, '\n'))) |
| 197 | *t = 0; | 202 | *t = 0; |
| 198 | lineparser(buf); | 203 | lineparser(buf); |
| 199 | } | 204 | } |
| @@ -201,77 +206,62 @@ loadcfg (char *file,int complain, void (*lineparser) (char *)) | |||
| 201 | fclose(fh); | 206 | fclose(fh); |
| 202 | } | 207 | } |
| 203 | 208 | ||
| 204 | void | 209 | void loadconfig(char *file) { loadcfg(file, 1, parsecfg); } |
| 205 | loadconfig (char *file) | ||
| 206 | { | ||
| 207 | loadcfg(file,1,parsecfg); | ||
| 208 | } | ||
| 209 | 210 | ||
| 210 | void | 211 | void loadformats(char *file) { loadcfg(file, 0, parseformats); } |
| 211 | loadformats (char *file) | ||
| 212 | { | ||
| 213 | loadcfg(file,0,parseformats); | ||
| 214 | } | ||
| 215 | 212 | ||
| 216 | /* get-format-string */ | 213 | /* get-format-string */ |
| 217 | char * | 214 | char *getformatstr(formtstr id) { |
| 218 | getformatstr (formtstr id) | ||
| 219 | { | ||
| 220 | int i; | 215 | int i; |
| 221 | for (i = 0; formatstrings[i].formatstr; i++) | 216 | for (i = 0; formatstrings[i].formatstr; i++) |
| 222 | if (formatstrings[i].id == id) return formatstrings[i].formatstr; | 217 | if (formatstrings[i].id == id) |
| 218 | return formatstrings[i].formatstr; | ||
| 223 | return NULL; | 219 | return NULL; |
| 224 | } | 220 | } |
| 225 | 221 | ||
| 226 | /* get-string-option, fetches *char-value of variable named by option */ | 222 | /* get-string-option, fetches *char-value of variable named by option */ |
| 227 | char * | 223 | char *getstroption(confopt option) { |
| 228 | getstroption (confopt option) | ||
| 229 | { | ||
| 230 | int i; | 224 | int i; |
| 231 | #ifdef DEBUG | 225 | #ifdef DEBUG |
| 232 | fprintf(stderr,"getstroption: %d\n",option); | 226 | fprintf(stderr, "getstroption: %d\n", option); |
| 233 | #endif | 227 | #endif |
| 234 | for (i = 0; configoptions[i].type != CO_NIL; i++) | 228 | for (i = 0; configoptions[i].type != CO_NIL; i++) |
| 235 | if ((configoptions[i].id == option) && (configoptions[i].type == CO_STR)) { | 229 | if ((configoptions[i].id == option) && (configoptions[i].type == CO_STR)) { |
| 236 | if (!configoptions[i].value) | 230 | if (!configoptions[i].value) |
| 237 | return configoptions[i].defaultvalue; | 231 | return configoptions[i].defaultvalue; |
| 238 | else | 232 | else |
| 239 | return configoptions[i].value; | 233 | return configoptions[i].value; |
| 240 | } | 234 | } |
| 241 | return NULL; | 235 | return NULL; |
| 242 | } | 236 | } |
| 243 | 237 | ||
| 244 | /* set-string-option, puts *char-value to variable named by option */ | 238 | /* set-string-option, puts *char-value to variable named by option */ |
| 245 | void | 239 | void setstroption(confopt option, char *string) { |
| 246 | setstroption (confopt option, char *string) | ||
| 247 | { | ||
| 248 | int i; | 240 | int i; |
| 249 | #ifdef DEBUG | 241 | #ifdef DEBUG |
| 250 | fprintf(stderr,"setstroption: %d to %s\n",option,string); | 242 | fprintf(stderr, "setstroption: %d to %s\n", option, string); |
| 251 | #endif | 243 | #endif |
| 252 | for (i = 0; configoptions[i].type != CO_NIL; i++) | 244 | for (i = 0; configoptions[i].type != CO_NIL; i++) |
| 253 | if ((configoptions[i].id == option) && (configoptions[i].type == CO_STR)) { | 245 | if ((configoptions[i].id == option) && (configoptions[i].type == CO_STR)) { |
| 254 | if (configoptions[i].value) | 246 | if (configoptions[i].value) |
| 255 | free(configoptions[i].value); | 247 | free(configoptions[i].value); |
| 256 | if (string) | 248 | if (string) |
| 257 | configoptions[i].value = strdup(string); | 249 | configoptions[i].value = strdup(string); |
| 258 | else | 250 | else |
| 259 | configoptions[i].value = NULL; | 251 | configoptions[i].value = NULL; |
| 260 | if (configoptions[i].localvar.pstr) | 252 | if (configoptions[i].localvar.pstr) |
| 261 | *configoptions[i].localvar.pstr = configoptions[i].value; | 253 | *configoptions[i].localvar.pstr = configoptions[i].value; |
| 262 | } | 254 | } |
| 263 | } | 255 | } |
| 264 | 256 | ||
| 265 | /* set-named-option, puts string to variable named by name */ | 257 | /* set-named-option, puts string to variable named by name */ |
| 266 | void | 258 | void setnoption(const char *name, char *string) { |
| 267 | setnoption (char *name, char *string) | ||
| 268 | { | ||
| 269 | int i; | 259 | int i; |
| 270 | #ifdef DEBUG | 260 | #ifdef DEBUG |
| 271 | fprintf(stderr,"setstrnoption: %s to %s\n",name,string); | 261 | fprintf(stderr, "setstrnoption: %s to %s\n", name, string); |
| 272 | #endif | 262 | #endif |
| 273 | for (i = 0; configoptions[i].type != CO_NIL; i++) | 263 | for (i = 0; configoptions[i].type != CO_NIL; i++) |
| 274 | if (!strcmp(configoptions[i].varname,name)) { | 264 | if (!strcmp(configoptions[i].varname, name)) { |
| 275 | if (configoptions[i].type == CO_STR) { | 265 | if (configoptions[i].type == CO_STR) { |
| 276 | if (configoptions[i].value) | 266 | if (configoptions[i].value) |
| 277 | free(configoptions[i].value); | 267 | free(configoptions[i].value); |
| @@ -280,276 +270,290 @@ setnoption (char *name, char *string) | |||
| 280 | else | 270 | else |
| 281 | configoptions[i].value = NULL; | 271 | configoptions[i].value = NULL; |
| 282 | if (configoptions[i].localvar.pstr) | 272 | if (configoptions[i].localvar.pstr) |
| 283 | *configoptions[i].localvar.pstr = configoptions[i].value; | 273 | *configoptions[i].localvar.pstr = configoptions[i].value; |
| 284 | } else if (configoptions[i].type == CO_INT) { | 274 | } else if (configoptions[i].type == CO_INT) { |
| 285 | configoptions[i].value = (char *)(uintptr_t)atoi(string); | 275 | configoptions[i].value = (char *)(uintptr_t)atoi(string); |
| 286 | if (configoptions[i].localvar.pint) | 276 | if (configoptions[i].localvar.pint) |
| 287 | *configoptions[i].localvar.pint = (uintptr_t)configoptions[i].value; | 277 | *configoptions[i].localvar.pint = (uintptr_t)configoptions[i].value; |
| 288 | } | 278 | } |
| 289 | } | 279 | } |
| 290 | } | 280 | } |
| 291 | 281 | ||
| 292 | /* get-integer-option, fetches int-value of variable named by option */ | 282 | /* get-integer-option, fetches int-value of variable named by option */ |
| 293 | int | 283 | int getintoption(confopt option) { |
| 294 | getintoption (confopt option) | ||
| 295 | { | ||
| 296 | int i; | 284 | int i; |
| 297 | #ifdef DEBUG | 285 | #ifdef DEBUG |
| 298 | fprintf(stderr,"getintoption: %d\n",option); | 286 | fprintf(stderr, "getintoption: %d\n", option); |
| 299 | #endif | 287 | #endif |
| 300 | for (i = 0; configoptions[i].type != CO_NIL; i++) | 288 | for (i = 0; configoptions[i].type != CO_NIL; i++) |
| 301 | if ((configoptions[i].id == option) && (configoptions[i].type == CO_INT)) { | 289 | if ((configoptions[i].id == option) && (configoptions[i].type == CO_INT)) { |
| 302 | if ((uintptr_t)configoptions[i].value == -1) | 290 | if ((intptr_t)configoptions[i].value == -1) |
| 303 | return (uintptr_t) configoptions[i].defaultvalue; | 291 | return (intptr_t)configoptions[i].defaultvalue; |
| 304 | else | 292 | else |
| 305 | return (uintptr_t) configoptions[i].value; | 293 | return (intptr_t)configoptions[i].value; |
| 306 | } | 294 | } |
| 307 | return 0; | 295 | return 0; |
| 308 | } | 296 | } |
| 309 | 297 | ||
| 310 | /* set-integer-option, puts int-value to variable named by option */ | 298 | /* set-integer-option, puts int-value to variable named by option */ |
| 311 | void | 299 | void setintoption(confopt option, int value) { |
| 312 | setintoption (confopt option, int value) | ||
| 313 | { | ||
| 314 | int i; | 300 | int i; |
| 315 | #ifdef DEBUG | 301 | #ifdef DEBUG |
| 316 | fprintf(stderr,"setintoption: %d to %d\n",option,value); | 302 | fprintf(stderr, "setintoption: %d to %d\n", option, value); |
| 317 | #endif | 303 | #endif |
| 318 | for (i = 0; configoptions[i].type != CO_NIL; i++) | 304 | for (i = 0; configoptions[i].type != CO_NIL; i++) |
| 319 | if ((configoptions[i].id == option) && (configoptions[i].type == CO_INT)) { | 305 | if ((configoptions[i].id == option) && (configoptions[i].type == CO_INT)) { |
| 320 | configoptions[i].value = (char *)(uintptr_t)value; | 306 | configoptions[i].value = (char *)(uintptr_t)value; |
| 321 | if (configoptions[i].localvar.pint) | 307 | if (configoptions[i].localvar.pint) |
| 322 | *configoptions[i].localvar.pint = (uintptr_t)configoptions[i].value; | 308 | *configoptions[i].localvar.pint = (uintptr_t)configoptions[i].value; |
| 323 | } | 309 | } |
| 324 | } | 310 | } |
| 325 | 311 | ||
| 326 | int quitrequest = 0; | 312 | int quitrequest = 0; |
| 327 | 313 | ||
| 328 | /* cleanup-hook, for SIGINT */ | 314 | /* cleanup-hook, for SIGINT */ |
| 329 | void | 315 | void cleanup(int signal) { |
| 330 | cleanup (int signal) | 316 | if (signal == SIGINT) { |
| 331 | { | 317 | switch (quitrequest >> 2) { |
| 332 | if( signal == SIGINT ) { | 318 | case 0: |
| 333 | switch( quitrequest >> 2 ) { | 319 | flushout(); |
| 334 | case 0: | 320 | writeout(" Press Ctrl+C twice now to confirm "); |
| 335 | flushout( ); | 321 | showout(); |
| 336 | writeout( " Press Ctrl+C twice now to confirm "); | 322 | quitrequest += 4; |
| 337 | showout( ); | 323 | return; |
| 338 | quitrequest+=4; | 324 | break; |
| 339 | return; | 325 | case 1: |
| 340 | break; | 326 | flushout(); |
| 341 | case 1: | 327 | writeout(" Press Ctrl+C twice now to confirm "); |
| 342 | flushout( ); | 328 | writeout(" Press Ctrl+C once now to confirm "); |
| 343 | writeout( " Press Ctrl+C twice now to confirm "); | 329 | showout(); |
| 344 | writeout( " Press Ctrl+C once now to confirm "); | 330 | quitrequest += 4; |
| 345 | showout( ); | 331 | return; |
| 346 | quitrequest+=4; | 332 | break; |
| 347 | return; | 333 | default: |
| 348 | break; | 334 | break; |
| 349 | default: | 335 | } |
| 350 | break; | ||
| 351 | } | ||
| 352 | } | 336 | } |
| 353 | /* restore terminal state */ | 337 | /* restore terminal state */ |
| 354 | exitui (); | 338 | exitui(); |
| 355 | /* clear userlist */ | 339 | /* clear userlist */ |
| 356 | ul_clear (); | 340 | ul_clear(); |
| 357 | /* close server connection */ | 341 | vc_disconnect(); |
| 358 | if (serverfd > 0) { | 342 | |
| 359 | close (serverfd); | ||
| 360 | serverfd = -1; | ||
| 361 | } | ||
| 362 | /* inform user if we where killed by signal */ | 343 | /* inform user if we where killed by signal */ |
| 363 | if (signal > 1) | 344 | if (signal > 1) { |
| 364 | { | 345 | fprintf(stderr, "vchat-client: terminated with signal %d.\n", signal); |
| 365 | fprintf (stderr, "vchat-client: terminated with signal %d.\n", signal); | 346 | if (!loggedin) |
| 366 | } else if (errstr[0]) | 347 | dumpconnect(); |
| 367 | fputs (errstr, stderr); | 348 | } else if (errstr[0]) { |
| 349 | fputs(errstr, stderr); | ||
| 350 | if (!loggedin) | ||
| 351 | dumpconnect(); | ||
| 352 | } | ||
| 368 | /* end of story */ | 353 | /* end of story */ |
| 369 | exit (0); | 354 | exit(0); |
| 370 | } | 355 | } |
| 371 | 356 | ||
| 372 | static int oldseconds = 0; | 357 | static int oldseconds = 0; |
| 373 | 358 | ||
| 374 | void calleverysecond( void ) { | 359 | void calleverysecond(void) { |
| 375 | /* timetriggered execution, don't rely on being called every 1000us */ | 360 | /* timetriggered execution, don't rely on being called every 1000us */ |
| 376 | /* rather see it as a chance for being called 9 times in 10 seconds */ | 361 | /* rather see it as a chance for being called 9 times in 10 seconds */ |
| 377 | /* so check time() */ | 362 | /* so check time() */ |
| 378 | time_t now = time( NULL ); | 363 | time_t now = time(NULL); |
| 379 | struct tm *mytime = localtime( &now ); | 364 | struct tm *mytime = localtime(&now); |
| 380 | if( mytime->tm_sec < oldseconds ) { | 365 | if (mytime->tm_sec < oldseconds) { |
| 381 | consoleline( NULL ); | 366 | consoleline(NULL); |
| 382 | } | 367 | } |
| 383 | oldseconds = mytime->tm_sec; | 368 | oldseconds = mytime->tm_sec; |
| 384 | 369 | ||
| 385 | if(quitrequest) | 370 | if (quitrequest) |
| 386 | quitrequest--; | 371 | quitrequest--; |
| 387 | if(outputcountdown && !--outputcountdown) | 372 | if (outputcountdown && !--outputcountdown) |
| 388 | hideout( ); | 373 | hideout(); |
| 389 | if( reconnect_time && ( time( NULL ) > reconnect_time ) ) | 374 | if (reconnect_time && (time(NULL) > reconnect_time)) |
| 390 | status = 0; | 375 | status = 0; |
| 391 | } | 376 | } |
| 392 | 377 | ||
| 393 | /* this function is called in the master loop */ | 378 | /* this function is called in the master loop */ |
| 394 | void | 379 | void eventloop(void) { |
| 395 | eventloop (void) | 380 | int poll_result = vc_poll(1 /* second timeout */); |
| 396 | { | 381 | |
| 397 | /* get fresh copy of filedescriptor list */ | 382 | switch (poll_result) { |
| 398 | fd_set readfds = masterfds; | ||
| 399 | struct timeval tv = { 1, 0}; | ||
| 400 | |||
| 401 | switch (select (serverfd + 2, &readfds, NULL, NULL, &tv)) | ||
| 402 | { | ||
| 403 | case -1: | 383 | case -1: |
| 404 | /* EINTR is most likely a SIGWINCH - ignore for now */ | 384 | /* EINTR is most likely a SIGWINCH - ignore for now */ |
| 405 | if (errno != EINTR) | 385 | if (errno != EINTR) { |
| 406 | { | 386 | snprintf(tmpstr, TMPSTRSIZE, "Select fails, %s.", strerror(errno)); |
| 407 | snprintf (tmpstr, TMPSTRSIZE, "Select fails, %s.", strerror(errno)); | 387 | strncpy(errstr, tmpstr, TMPSTRSIZE - 2); |
| 408 | strncpy(errstr,tmpstr,TMPSTRSIZE-2); | 388 | errstr[TMPSTRSIZE - 2] = '\0'; |
| 409 | errstr[TMPSTRSIZE-2] = '\0'; | 389 | strcat(errstr, "\n"); |
| 410 | strcat(errstr,"\n"); | 390 | writecf(FS_ERR, tmpstr); |
| 411 | writecf (FS_ERR,tmpstr); | 391 | /* see this as an error condition and bail out */ |
| 412 | /* see this as an error condition and bail out */ | 392 | status = 0; |
| 413 | status = 0; | 393 | } |
| 414 | } | 394 | break; |
| 415 | break; | ||
| 416 | case 0: | 395 | case 0: |
| 417 | /* time out reached */ | 396 | /* time out reached */ |
| 418 | calleverysecond(); | 397 | calleverysecond(); |
| 419 | break; | 398 | break; |
| 420 | default: | 399 | default: |
| 421 | /* something to read from user & we're logged in or have a cert? */ | 400 | /* something to read from user & we're logged in or have a cert? */ |
| 422 | if (FD_ISSET (0, &readfds) ) | 401 | if (poll_result & 1) |
| 423 | userinput (); | 402 | userinput(); |
| 424 | 403 | ||
| 425 | /* something to read from server? */ | 404 | /* something to read from server? */ |
| 426 | if (serverfd!=-1 && FD_ISSET (serverfd, &readfds)) | 405 | if ((poll_result & 2) && vc_receive()) |
| 427 | networkinput (); | 406 | status = 0; |
| 428 | break; | 407 | break; |
| 429 | } | 408 | } |
| 430 | } | 409 | } |
| 431 | 410 | ||
| 432 | void usage( char *name) { | 411 | void usage(char *name) { |
| 433 | printf ("usage: %s [-C config-file] [-F formats] [-l] [-z] [-s host] [-p port] [-c channel] [-n nickname]\n",name); | 412 | printf("usage: %s [-C config-file] [-F formats] [-l] [-z] [-s host] [-p " |
| 434 | puts (" -C load a second config-file, overriding the first one"); | 413 | "port] [-c channel] [-n nickname]\n", |
| 435 | puts (" -F load format strings (skins) from this file"); | 414 | name); |
| 436 | puts (" -l local connect (no SSL)"); | 415 | puts(" -C load a second config-file, overriding the first one"); |
| 437 | puts (" -z don't use certificate files"); | 416 | puts(" -F load format strings (skins) from this file"); |
| 438 | printf (" -s set server (default \"%s\")\n",getstroption(CF_SERVERHOST)); | 417 | puts(" -l local connect (no SSL)"); |
| 439 | printf (" -p set port (default %s)\n",getstroption(CF_SERVERPORT)); | 418 | puts(" -z don't use certificate files"); |
| 440 | printf (" -c set channel (default %d)\n",getintoption(CF_CHANNEL)); | 419 | printf(" -s set server (default \"%s\")\n", getstroption(CF_SERVERHOST)); |
| 441 | if (own_nick_get()) | 420 | printf(" -p set port (default %s)\n", getstroption(CF_SERVERPORT)); |
| 442 | printf(" -n set nickname (default \"%s\")\n",own_nick_get()); | 421 | printf(" -c set channel (default %d)\n", getintoption(CF_CHANNEL)); |
| 443 | else | 422 | if (own_nick_get()) |
| 444 | puts (" -n set nickname"); | 423 | printf(" -n set nickname (default \"%s\")\n", own_nick_get()); |
| 445 | printf (" -f set from (default \"%s\")\n",getstroption(CF_FROM)); | 424 | else |
| 446 | puts (" -h gives this help"); | 425 | puts(" -n set nickname"); |
| 447 | puts (" -v show module versions"); | 426 | printf(" -f set from (default \"%s\")\n", getstroption(CF_FROM)); |
| 427 | puts(" -h gives this help"); | ||
| 428 | puts(" -v show module versions"); | ||
| 448 | } | 429 | } |
| 449 | 430 | ||
| 450 | void versions() { | 431 | void versions() { |
| 451 | puts (vchat_cl_version); | 432 | puts(vchat_cl_version); |
| 452 | puts (vchat_ui_version); | 433 | puts(vchat_ui_version); |
| 453 | puts (vchat_io_version); | 434 | puts(vchat_io_version); |
| 454 | puts (vchat_us_version); | 435 | puts(vchat_us_version); |
| 455 | puts (vchat_cm_version); | 436 | puts(vchat_cm_version); |
| 456 | puts (vchat_ssl_version); | 437 | puts(vchat_tls_version); |
| 457 | puts (vchat_ssl_version_external); | 438 | puts(vchat_tls_version_external()); |
| 458 | } | 439 | } |
| 459 | 440 | ||
| 460 | /* main - d'oh */ | 441 | /* main - d'oh */ |
| 461 | int | 442 | int main(int argc, char **argv) { |
| 462 | main (int argc, char **argv) | ||
| 463 | { | ||
| 464 | int pchar; | 443 | int pchar; |
| 465 | int cmdsunparsed = 1; | 444 | int cmdsunparsed = 1; |
| 466 | 445 | ||
| 467 | setlocale(LC_ALL,""); | 446 | setlocale(LC_ALL, ""); |
| 468 | |||
| 469 | loadconfig (GLOBAL_CONFIG_FILE); | ||
| 470 | loadconfig (getstroption (CF_CONFIGFILE)); | ||
| 471 | 447 | ||
| 472 | /* make SSL version used visible */ | 448 | loadconfig(GLOBAL_CONFIG_FILE); |
| 473 | vchat_ssl_get_version_external(); | 449 | loadconfig(getstroption(CF_CONFIGFILE)); |
| 474 | 450 | ||
| 475 | /* parse commandline */ | 451 | /* parse commandline */ |
| 476 | while (cmdsunparsed) { | 452 | while (cmdsunparsed) { |
| 477 | pchar = getopt(argc,argv,"C:F:lzs:p:c:n:f:kKL:hv"); | 453 | pchar = getopt(argc, argv, "C:F:lzs:p:c:n:f:kKL:hv"); |
| 478 | #ifdef DEBUG | 454 | #ifdef DEBUG |
| 479 | fprintf(stderr,"parse commandline: %d ('%c'): %s\n",pchar,pchar,optarg); | 455 | fprintf(stderr, "parse commandline: %d ('%c'): %s\n", pchar, pchar, optarg); |
| 480 | #endif | 456 | #endif |
| 481 | 457 | ||
| 482 | switch (pchar) { | 458 | switch (pchar) { |
| 483 | case -1 : cmdsunparsed = 0; break; | 459 | case -1: |
| 484 | case 'C': loadconfig(optarg); break; | 460 | cmdsunparsed = 0; |
| 485 | case 'F': setstroption(CF_FORMFILE,optarg); break; | 461 | break; |
| 486 | case 'l': setintoption(CF_USESSL,0); break; | 462 | case 'C': |
| 487 | case 'z': setintoption(CF_USECERT,0); break; | 463 | loadconfig(optarg); |
| 488 | case 's': setstroption(CF_SERVERHOST,optarg); break; | 464 | break; |
| 489 | case 'p': setstroption(CF_SERVERPORT,optarg); break; | 465 | case 'F': |
| 490 | case 'c': setintoption(CF_CHANNEL,strtol(optarg,NULL,10)); break; | 466 | setstroption(CF_FORMFILE, optarg); |
| 491 | case 'n': own_nick_set(optarg); break; | 467 | break; |
| 492 | case 'f': setstroption(CF_FROM,optarg); break; | 468 | case 'l': |
| 493 | case 'h': usage(argv[0]); exit(0); break; | 469 | setintoption(CF_USESSL, 0); |
| 494 | case 'v': versions(); exit(0); break; | 470 | break; |
| 495 | default : usage(argv[0]); exit(1); | 471 | case 'z': |
| 496 | } | 472 | setintoption(CF_USECERT, 0); |
| 473 | break; | ||
| 474 | case 's': | ||
| 475 | setstroption(CF_SERVERHOST, optarg); | ||
| 476 | break; | ||
| 477 | case 'p': | ||
| 478 | setstroption(CF_SERVERPORT, optarg); | ||
| 479 | break; | ||
| 480 | case 'c': | ||
| 481 | setintoption(CF_CHANNEL, strtol(optarg, NULL, 10)); | ||
| 482 | break; | ||
| 483 | case 'n': | ||
| 484 | own_nick_set(optarg); | ||
| 485 | break; | ||
| 486 | case 'f': | ||
| 487 | setstroption(CF_FROM, optarg); | ||
| 488 | break; | ||
| 489 | case 'h': | ||
| 490 | usage(argv[0]); | ||
| 491 | exit(0); | ||
| 492 | break; | ||
| 493 | case 'v': | ||
| 494 | versions(); | ||
| 495 | exit(0); | ||
| 496 | break; | ||
| 497 | default: | ||
| 498 | usage(argv[0]); | ||
| 499 | exit(1); | ||
| 500 | } | ||
| 497 | } | 501 | } |
| 498 | 502 | ||
| 499 | if (optind < argc) { usage(argv[0]); exit(1); } | 503 | if (optind < argc) { |
| 504 | usage(argv[0]); | ||
| 505 | exit(1); | ||
| 506 | } | ||
| 500 | 507 | ||
| 501 | loadformats(GLOBAL_FORMAT_FILE); | 508 | loadformats(GLOBAL_FORMAT_FILE); |
| 502 | loadformats(getstroption (CF_FORMFILE)); | 509 | loadformats(getstroption(CF_FORMFILE)); |
| 503 | 510 | ||
| 504 | /* install signal handler */ | 511 | /* install signal handler */ |
| 505 | signal (SIGINT, cleanup); | 512 | signal(SIGINT, cleanup); |
| 506 | signal (SIGHUP, cleanup); | 513 | signal(SIGHUP, cleanup); |
| 507 | signal (SIGTERM, cleanup); | 514 | signal(SIGTERM, cleanup); |
| 508 | signal (SIGQUIT, cleanup); | 515 | signal(SIGQUIT, SIG_IGN); |
| 509 | 516 | ||
| 510 | /* initialize userinterface */ | 517 | /* initialize userinterface */ |
| 511 | initui (); | 518 | initui(); |
| 512 | |||
| 513 | while( status ) { | ||
| 514 | /* add stdin to masterfds */ | ||
| 515 | FD_ZERO (&masterfds); | ||
| 516 | FD_SET (0, &masterfds); | ||
| 517 | 519 | ||
| 520 | while (status) { | ||
| 518 | /* attempt connection */ | 521 | /* attempt connection */ |
| 519 | if (vcconnect (getstroption(CF_SERVERHOST), getstroption(CF_SERVERPORT))) { | 522 | if (vc_connect(getstroption(CF_SERVERHOST), getstroption(CF_SERVERPORT))) { |
| 520 | snprintf (tmpstr, TMPSTRSIZE, "Could not connect to server, %s.", strerror(errno)); | 523 | snprintf(tmpstr, TMPSTRSIZE, "Could not connect to server, %s.", |
| 521 | strncpy(errstr,tmpstr,TMPSTRSIZE-2); | 524 | strerror(errno)); |
| 522 | errstr[TMPSTRSIZE-2] = '\0'; | 525 | strncpy(errstr, tmpstr, TMPSTRSIZE - 2); |
| 523 | strcat(errstr,"\n"); | 526 | errstr[TMPSTRSIZE - 2] = '\0'; |
| 524 | writecf (FS_ERR,tmpstr); | 527 | strcat(errstr, "\n"); |
| 525 | 528 | writecf(FS_ERR, tmpstr); | |
| 526 | if( getintoption( CF_AUTORECONN ) ) { | 529 | |
| 527 | snprintf (tmpstr, TMPSTRSIZE, "reconnecting in %d seconds", reconnect_delay ); | 530 | if (getintoption(CF_AUTORECONN)) { |
| 528 | writecf (FS_ERR, tmpstr); | 531 | snprintf(tmpstr, TMPSTRSIZE, "reconnecting in %d seconds", |
| 529 | reconnect_delay = ( reconnect_delay * 15 ) / 10; | 532 | reconnect_delay); |
| 530 | reconnect_time = time( NULL ) + reconnect_delay; | 533 | writecf(FS_ERR, tmpstr); |
| 534 | reconnect_delay = (reconnect_delay * 15) / 10; | ||
| 535 | reconnect_time = time(NULL) + reconnect_delay; | ||
| 531 | } else | 536 | } else |
| 532 | status = 0; | 537 | status = 0; |
| 533 | } else { | 538 | } else { |
| 534 | /* add serverfd to masterfds, reset reconnect delay */ | 539 | /* reset reconnect delay */ |
| 535 | FD_SET (serverfd, &masterfds); | ||
| 536 | reconnect_delay = 6; | 540 | reconnect_delay = 6; |
| 537 | reconnect_time = 0; | 541 | reconnect_time = 0; |
| 538 | } | 542 | } |
| 539 | 543 | ||
| 540 | while (status) | 544 | while (status) |
| 541 | eventloop (); | 545 | eventloop(); |
| 542 | 546 | ||
| 543 | /* sanely close connection to server */ | 547 | /* sanely close connection to server */ |
| 544 | vcdisconnect (); | 548 | vc_disconnect(); |
| 545 | 549 | ||
| 546 | if( !ownquit && ( getintoption( CF_AUTORECONN ) || wantreconnect ) ) | 550 | if (!ownquit && (getintoption(CF_AUTORECONN) || wantreconnect)) |
| 547 | status = 1; | 551 | status = 1; |
| 548 | 552 | ||
| 549 | wantreconnect = 0; | 553 | wantreconnect = 0; |
| 550 | } | 554 | } |
| 551 | 555 | ||
| 552 | /* call cleanup-hook without signal */ | 556 | /* call cleanup-hook without signal */ |
| 553 | cleanup (0); | 557 | cleanup(0); |
| 554 | return 0; | 558 | return 0; |
| 555 | } | 559 | } |
diff --git a/vchat-client.sgml b/vchat-client.sgml index 3d6fbc7..7e96b28 100755..100644 --- a/vchat-client.sgml +++ b/vchat-client.sgml | |||
| @@ -101,7 +101,7 @@ overridden in the configfile.</para></listitem> | |||
| 101 | 101 | ||
| 102 | <varlistentry> | 102 | <varlistentry> |
| 103 | <term><option>-f</option> <replaceable>from</replaceable></term> | 103 | <term><option>-f</option> <replaceable>from</replaceable></term> |
| 104 | <listitem><para>set from (default "vc-alpha-0.19")</para></listitem> | 104 | <listitem><para>set from (default "vc-alpha-0.20")</para></listitem> |
| 105 | </varlistentry> | 105 | </varlistentry> |
| 106 | 106 | ||
| 107 | <varlistentry> | 107 | <varlistentry> |
diff --git a/vchat-commands.c b/vchat-commands.c index 06c9010..3ef3132 100755..100644 --- a/vchat-commands.c +++ b/vchat-commands.c | |||
| @@ -10,26 +10,29 @@ | |||
| 10 | * without even the implied warranty of merchantability or fitness for a | 10 | * without even the implied warranty of merchantability or fitness for a |
| 11 | * particular purpose. In no event shall the copyright holder be liable for | 11 | * particular purpose. In no event shall the copyright holder be liable for |
| 12 | * any direct, indirect, incidental or special damages arising in any way out | 12 | * any direct, indirect, incidental or special damages arising in any way out |
| 13 | * of the use of this software. | 13 | * of the use of this software. |
| 14 | * | 14 | * |
| 15 | */ | 15 | */ |
| 16 | 16 | ||
| 17 | /* general includes */ | 17 | /* general includes */ |
| 18 | #include <stdlib.h> | ||
| 19 | #include <unistd.h> | ||
| 20 | #include <errno.h> | 18 | #include <errno.h> |
| 21 | #include <stdio.h> | 19 | #include <stdio.h> |
| 20 | #include <stdlib.h> | ||
| 22 | #include <string.h> | 21 | #include <string.h> |
| 23 | #include <sys/stat.h> | 22 | #include <sys/stat.h> |
| 23 | #include <unistd.h> | ||
| 24 | |||
| 24 | #include <readline/readline.h> | 25 | #include <readline/readline.h> |
| 25 | 26 | ||
| 26 | /* local includes */ | 27 | /* local includes */ |
| 27 | #include "vchat.h" | 28 | #include "vchat-connection.h" |
| 28 | #include "vchat-help.h" | 29 | #include "vchat-help.h" |
| 29 | #include "vchat-user.h" | 30 | #include "vchat-user.h" |
| 31 | #include "vchat.h" | ||
| 30 | 32 | ||
| 31 | /* version of this module */ | 33 | /* version of this module */ |
| 32 | const char *vchat_cm_version = "vchat-commands.c $Id$"; | 34 | const char *vchat_cm_version = |
| 35 | "vchat-commands.c $Id$"; | ||
| 33 | 36 | ||
| 34 | /* from vchat-client.c */ | 37 | /* from vchat-client.c */ |
| 35 | extern int ownquit; | 38 | extern int ownquit; |
| @@ -38,45 +41,45 @@ extern int status; | |||
| 38 | 41 | ||
| 39 | /* our "/command " table */ | 42 | /* our "/command " table */ |
| 40 | enum { | 43 | enum { |
| 41 | COMMAND_VERSION, | 44 | COMMAND_VERSION, |
| 42 | COMMAND_FILTERS, | 45 | COMMAND_FILTERS, |
| 43 | COMMAND_LSFLT, | 46 | COMMAND_LSFLT, |
| 44 | COMMAND_RMFLT, | 47 | COMMAND_RMFLT, |
| 45 | COMMAND_CLFLT, | 48 | COMMAND_CLFLT, |
| 46 | COMMAND_HELP, | 49 | COMMAND_HELP, |
| 47 | COMMAND_FORMAT, | 50 | COMMAND_FORMAT, |
| 48 | COMMAND_KEYS, | 51 | COMMAND_KEYS, |
| 49 | COMMAND_QUIT, | 52 | COMMAND_QUIT, |
| 50 | COMMAND_USER, | 53 | COMMAND_USER, |
| 51 | COMMAND_DICT, | 54 | COMMAND_DICT, |
| 52 | COMMAND_FLT, | 55 | COMMAND_FLT, |
| 53 | COMMAND_PM, | 56 | COMMAND_PM, |
| 54 | COMMAND_ACTION, | 57 | COMMAND_ACTION, |
| 55 | COMMAND_PMSHORT, | 58 | COMMAND_PMSHORT, |
| 56 | COMMAND_QUERY, | 59 | COMMAND_QUERY, |
| 57 | COMMAND_QUITSHORT, | 60 | COMMAND_QUITSHORT, |
| 58 | COMMAND_PLAIN, | 61 | COMMAND_PLAIN, |
| 59 | COMMAND_RECONNECT, | 62 | COMMAND_RECONNECT, |
| 60 | COMMAND_NONE | 63 | COMMAND_NONE |
| 61 | }; | 64 | }; |
| 62 | 65 | ||
| 63 | static void command_quit ( char *tail); | 66 | static void command_quit(char *tail); |
| 64 | static void command_user ( char *tail); | 67 | static void command_user(char *tail); |
| 65 | static void command_pm ( char *tail); | 68 | static void command_pm(char *tail); |
| 66 | static void command_action ( char *tail); | 69 | static void command_action(char *tail); |
| 67 | static void command_help ( char *tail); | 70 | static void command_help(char *tail); |
| 68 | static void command_format ( char *tail); | 71 | static void command_format(char *tail); |
| 69 | static void command_flt ( char *tail); | 72 | static void command_flt(char *tail); |
| 70 | static void command_lsflt ( char *tail); | 73 | static void command_lsflt(char *tail); |
| 71 | static void command_clflt ( char *tail); | 74 | static void command_clflt(char *tail); |
| 72 | static void command_rmflt ( char *tail); | 75 | static void command_rmflt(char *tail); |
| 73 | void command_version ( char *tail); | 76 | void command_version(char *tail); |
| 74 | static void command_none ( char *line); | 77 | static void command_none(char *line); |
| 75 | static void command_query ( char *tail); | 78 | static void command_query(char *tail); |
| 76 | static void command_reconnect ( char *tail); | 79 | static void command_reconnect(char *tail); |
| 77 | static void command_dict ( char *tail); | 80 | static void command_dict(char *tail); |
| 78 | 81 | ||
| 79 | static void output_default ( char *tail); | 82 | static void output_default(char *tail); |
| 80 | 83 | ||
| 81 | /* commandentry defined in vchat.h */ | 84 | /* commandentry defined in vchat.h */ |
| 82 | 85 | ||
| @@ -105,9 +108,7 @@ commandtable[] = { | |||
| 105 | }; | 108 | }; |
| 106 | 109 | ||
| 107 | /* parse "/command" */ | 110 | /* parse "/command" */ |
| 108 | static int | 111 | static int translatecommand(char **cmd) { |
| 109 | translatecommand( char **cmd) | ||
| 110 | { | ||
| 111 | int result; | 112 | int result; |
| 112 | int cut = 0; | 113 | int cut = 0; |
| 113 | int maxcut = 0; | 114 | int maxcut = 0; |
| @@ -115,214 +116,215 @@ translatecommand( char **cmd) | |||
| 115 | /* We do only want to allow Command abbrevation to | 116 | /* We do only want to allow Command abbrevation to |
| 116 | the next newline, so that /VRES won't expand to /V RES */ | 117 | the next newline, so that /VRES won't expand to /V RES */ |
| 117 | 118 | ||
| 118 | while( (*cmd)[maxcut] && ((*cmd)[maxcut] != 0x20) && ((*cmd)[maxcut] != '\n')) maxcut++; | 119 | while ((*cmd)[maxcut] && ((*cmd)[maxcut] != 0x20) && ((*cmd)[maxcut] != '\n')) |
| 119 | if( maxcut ) maxcut--; | 120 | maxcut++; |
| 121 | if (maxcut) | ||
| 122 | maxcut--; | ||
| 120 | 123 | ||
| 121 | /* Repeatedly scan command table for command, with growing abbrevation cut off */ | 124 | /* Repeatedly scan command table for command, with growing abbrevation cut off |
| 125 | */ | ||
| 122 | do { | 126 | do { |
| 123 | /* Looks ugly, needs rewrite for better understanding */ | 127 | /* Looks ugly, needs rewrite for better understanding */ |
| 124 | for( result = 0; | 128 | for (result = 0; |
| 125 | (result != COMMAND_NONE) && | 129 | (result != COMMAND_NONE) && |
| 126 | (strncasecmp(*cmd, commandtable[result].name, commandtable[result].len - | 130 | (strncasecmp( |
| 127 | ((commandtable[result].len - maxcut - cut > 0) ? cut : 0))); | 131 | *cmd, commandtable[result].name, |
| 128 | result++); | 132 | commandtable[result].len - |
| 129 | } while ((cut < commandtable[0].len) && (commandtable[result].number == COMMAND_NONE) && (++cut)); | 133 | ((commandtable[result].len - maxcut - cut > 0) ? cut : 0))); |
| 134 | result++) | ||
| 135 | ; | ||
| 136 | } while ((cut < commandtable[0].len) && | ||
| 137 | (commandtable[result].number == COMMAND_NONE) && (++cut)); | ||
| 130 | 138 | ||
| 131 | /* Just leave the tail... */ | 139 | /* Just leave the tail... */ |
| 132 | (*cmd) += commandtable[result].len; | 140 | (*cmd) += commandtable[result].len; |
| 133 | 141 | ||
| 134 | /* ... whose start may be affected by abbrevation */ | 142 | /* ... whose start may be affected by abbrevation */ |
| 135 | if( commandtable[result].number != COMMAND_NONE ) | 143 | if (commandtable[result].number != COMMAND_NONE) |
| 136 | (*cmd) -= cut; | 144 | (*cmd) -= cut; |
| 137 | 145 | ||
| 138 | return result; | 146 | return result; |
| 139 | } | 147 | } |
| 140 | 148 | ||
| 141 | /* handle thought */ | 149 | /* handle thought */ |
| 142 | static void | 150 | static void dothink(const char *tail, const char nice) { |
| 143 | dothink( char *tail, char nice ) | 151 | while (*tail == ' ') |
| 144 | { | 152 | tail++; |
| 145 | while( *tail == ' ' ) tail++; | ||
| 146 | 153 | ||
| 147 | /* send users message to server */ | 154 | /* send users message to server */ |
| 148 | snprintf (tmpstr, TMPSTRSIZE, ".%c %s", nice, tail); | 155 | snprintf(tmpstr, TMPSTRSIZE, ".%c %s", nice, tail); |
| 149 | networkoutput (tmpstr); | 156 | vc_sendmessage(tmpstr); |
| 150 | 157 | ||
| 151 | /* show action in channel window */ | 158 | /* show action in channel window */ |
| 152 | snprintf (tmpstr, TMPSTRSIZE, nice == 'O' ? getformatstr(FS_TXPUBNTHOUGHT) : getformatstr(FS_TXPUBTHOUGHT), tail); | 159 | snprintf(tmpstr, TMPSTRSIZE, |
| 153 | writechan (tmpstr); | 160 | nice == 'O' ? getformatstr(FS_TXPUBNTHOUGHT) |
| 161 | : getformatstr(FS_TXPUBTHOUGHT), | ||
| 162 | tail); | ||
| 163 | writechan(tmpstr); | ||
| 154 | } | 164 | } |
| 155 | 165 | ||
| 156 | |||
| 157 | /* handle action */ | 166 | /* handle action */ |
| 158 | static void | 167 | static void doaction(const char *tail) { |
| 159 | doaction( char *tail ) | 168 | while (*tail == ' ') |
| 160 | { | 169 | tail++; |
| 161 | while( *tail == ' ' ) tail++; | 170 | |
| 162 | 171 | if (*tail) { | |
| 163 | if( *tail ) { | 172 | /* send users message to server */ |
| 164 | /* send users message to server */ | 173 | snprintf(tmpstr, TMPSTRSIZE, ".a %s", tail); |
| 165 | snprintf (tmpstr, TMPSTRSIZE, ".a %s", tail); | 174 | vc_sendmessage(tmpstr); |
| 166 | networkoutput (tmpstr); | 175 | |
| 167 | 176 | /* show action in channel window */ | |
| 168 | /* show action in channel window */ | 177 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_TXPUBACTION), own_nick_get(), |
| 169 | snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_TXPUBACTION), own_nick_get(), tail); | 178 | tail); |
| 170 | writechan (tmpstr); | 179 | writechan(tmpstr); |
| 171 | } else { | 180 | } else { |
| 172 | /* missing action */ | 181 | /* missing action */ |
| 173 | msgout( " You do nothing. " ); | 182 | msgout(" You do nothing. "); |
| 174 | } | 183 | } |
| 175 | } | 184 | } |
| 176 | 185 | ||
| 177 | /* handle private message outgoing */ | 186 | /* handle private message outgoing */ |
| 178 | static void | 187 | static void privatemessagetx(char *tail) { |
| 179 | privatemessagetx ( char *tail ) { | ||
| 180 | char *mesg; | 188 | char *mesg; |
| 181 | 189 | ||
| 182 | /* find nick */ | 190 | /* find nick */ |
| 183 | while( *tail==' ') tail++; | 191 | while (*tail == ' ') |
| 192 | tail++; | ||
| 184 | 193 | ||
| 185 | /* find message */ | 194 | /* find message */ |
| 186 | mesg = tail; | 195 | mesg = tail; |
| 187 | while ( *mesg && *mesg!=' ') mesg++; | 196 | while (*mesg && *mesg != ' ') |
| 197 | mesg++; | ||
| 188 | 198 | ||
| 189 | /* check for nick && message */ | 199 | /* check for nick && message */ |
| 190 | if(*tail && *mesg) { | 200 | if (*tail && *mesg) { |
| 191 | 201 | ||
| 192 | /* terminate nick, move to rel start */ | 202 | /* terminate nick, move to rel start */ |
| 193 | *mesg++ = '\0'; | 203 | *mesg++ = '\0'; |
| 194 | 204 | ||
| 195 | /* form message and send to server */ | 205 | /* form message and send to server */ |
| 196 | snprintf (tmpstr, TMPSTRSIZE, ".m %s %s", tail, mesg); | 206 | snprintf(tmpstr, TMPSTRSIZE, ".m %s %s", tail, mesg); |
| 197 | networkoutput (tmpstr); | 207 | vc_sendmessage(tmpstr); |
| 198 | 208 | ||
| 199 | /* show message in private window */ | 209 | /* show message in private window */ |
| 200 | snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_TXPRIVMSG), tail, mesg); | 210 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_TXPRIVMSG), tail, mesg); |
| 201 | writepriv (tmpstr, 0); | 211 | writepriv(tmpstr, 0); |
| 202 | 212 | ||
| 203 | /* note we messaged someone */ | 213 | /* note we messaged someone */ |
| 204 | ul_private_action(tail); | 214 | ul_private_action(tail); |
| 205 | 215 | ||
| 206 | } else { | 216 | } else { |
| 207 | /* Bump user to fill in missing parts */ | 217 | /* Bump user to fill in missing parts */ |
| 208 | msgout( *tail ? " Won't send empty message. ":" Recipient missing. " ); | 218 | msgout(*tail ? " Won't send empty message. " : " Recipient missing. "); |
| 209 | } | 219 | } |
| 210 | } | 220 | } |
| 211 | 221 | ||
| 212 | /* handle line entered by user */ | 222 | /* handle line entered by user */ |
| 213 | void | 223 | void handleline(char *line) { |
| 214 | handleline (char *line) | ||
| 215 | { | ||
| 216 | #ifdef DEBUG | 224 | #ifdef DEBUG |
| 217 | /* debugging? log users input! */ | 225 | /* debugging? log users input! */ |
| 218 | fprintf (stderr, "=| %s\n", line); | 226 | fprintf(stderr, "=| %s\n", line); |
| 219 | #endif | 227 | #endif |
| 220 | 228 | ||
| 221 | switch ( line[0] ) | 229 | switch (line[0]) { |
| 222 | { | ||
| 223 | case '.': | 230 | case '.': |
| 224 | switch ( line[1] ) { | 231 | switch (line[1]) { |
| 225 | case 'm': /* sending a private message? */ | 232 | case 'm': /* sending a private message? */ |
| 226 | privatemessagetx( line+2 ); | 233 | privatemessagetx(line + 2); |
| 227 | break; | 234 | break; |
| 228 | case 'a': /* Do an action */ | 235 | case 'a': /* Do an action */ |
| 229 | doaction( line+2 ); | 236 | doaction(line + 2); |
| 230 | break; | 237 | break; |
| 231 | case '.': | 238 | case '.': |
| 232 | /* .. on start of line is public */ | 239 | /* .. on start of line is public */ |
| 233 | if( line[2] != 'm' ) { | 240 | if (line[2] != 'm') { |
| 234 | output_default( line ); | 241 | output_default(line); |
| 235 | } else { | 242 | } else { |
| 236 | /* oopsi, "..m " detected */ | 243 | /* oopsi, "..m " detected */ |
| 237 | /* dunno what to do */ | 244 | /* dunno what to do */ |
| 238 | flushout( ); | 245 | flushout(); |
| 239 | writeout("? You probably misstyped ?"); | 246 | writeout("? You probably misstyped ?"); |
| 240 | writeout(" "); | 247 | writeout(" "); |
| 241 | writeout(line ); | 248 | writeout(line); |
| 242 | showout( ); | 249 | showout(); |
| 243 | } | ||
| 244 | break; | ||
| 245 | case 'o': | ||
| 246 | case 'O': | ||
| 247 | dothink( line + 2, line[1] ); | ||
| 248 | break; | ||
| 249 | case 'x': | ||
| 250 | /* inform vchat-client, that the following connection | ||
| 251 | drop was intentional */ | ||
| 252 | ownquit = 1; /* fallthrough intended */ | ||
| 253 | default: | ||
| 254 | /* generic server command, send to server, show to user */ | ||
| 255 | snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_COMMAND), line); | ||
| 256 | writechan (tmpstr); | ||
| 257 | networkoutput (line); | ||
| 258 | break; | ||
| 259 | } | 250 | } |
| 260 | break; | 251 | break; |
| 261 | case '/': | 252 | case 'o': |
| 262 | line++; | 253 | case 'O': |
| 263 | commandtable[translatecommand(&line)].handler(line); | 254 | dothink(line + 2, line[1]); |
| 264 | break; | 255 | break; |
| 265 | default: | 256 | case 'x': |
| 266 | output_default( line ); | 257 | /* inform vchat-client, that the following connection |
| 258 | drop was intentional */ | ||
| 259 | ownquit = 1; /* fallthrough intended */ | ||
| 260 | default: | ||
| 261 | /* generic server command, send to server, show to user */ | ||
| 262 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_COMMAND), line); | ||
| 263 | writechan(tmpstr); | ||
| 264 | vc_sendmessage(line); | ||
| 267 | break; | 265 | break; |
| 266 | } | ||
| 267 | break; | ||
| 268 | case '/': | ||
| 269 | line++; | ||
| 270 | commandtable[translatecommand(&line)].handler(line); | ||
| 271 | break; | ||
| 272 | default: | ||
| 273 | output_default(line); | ||
| 274 | break; | ||
| 268 | } | 275 | } |
| 269 | } | 276 | } |
| 270 | 277 | ||
| 271 | static void | 278 | static void output_default(char *line) { |
| 272 | output_default(char *line ) { | 279 | /* prepare for output on display */ |
| 273 | /* prepare for output on display */ | 280 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_TXPUBMSG), own_nick_get(), line); |
| 274 | snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_TXPUBMSG), own_nick_get(), line); | ||
| 275 | 281 | ||
| 276 | /* send original line to server */ | 282 | /* send original line to server */ |
| 277 | networkoutput (line); | 283 | vc_sendmessage(line); |
| 278 | 284 | ||
| 279 | /* output message to channel window */ | 285 | /* output message to channel window */ |
| 280 | writechan (tmpstr); | 286 | writechan(tmpstr); |
| 281 | } | 287 | } |
| 282 | 288 | ||
| 283 | /* handle a "/user " request */ | 289 | /* handle a "/user " request */ |
| 284 | static void | 290 | static void command_user(char *tail) { |
| 285 | command_user(char *tail) | 291 | while (*tail == ' ') |
| 286 | { | 292 | tail++; |
| 287 | while( *tail == ' ') tail++; | 293 | if (*tail) { |
| 288 | if( *tail ) { | 294 | char *out = ul_match_user(tail); |
| 289 | char * out = ul_match_user( tail); | 295 | if (*out) { |
| 290 | if( *out ) { | 296 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_USMATCH), tail, out); |
| 291 | snprintf( tmpstr, TMPSTRSIZE, getformatstr(FS_USMATCH), tail, out); | 297 | } else { |
| 292 | } else { | 298 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_ERR), |
| 293 | snprintf( tmpstr, TMPSTRSIZE, getformatstr(FS_ERR), " No user matched that regex. "); | 299 | " No user matched that regex. "); |
| 294 | } | 300 | } |
| 295 | } else { | 301 | } else { |
| 296 | snprintf( tmpstr, TMPSTRSIZE, getformatstr(FS_ERR), " Which user? "); | 302 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_ERR), " Which user? "); |
| 297 | } | 303 | } |
| 298 | msgout( tmpstr ); | 304 | msgout(tmpstr); |
| 299 | } | 305 | } |
| 300 | 306 | ||
| 301 | /* handle a "/msg " request */ | 307 | /* handle a "/msg " request */ |
| 302 | static void | 308 | static void command_pm(char *tail) { privatemessagetx(tail); } |
| 303 | command_pm (char *tail) | ||
| 304 | { | ||
| 305 | privatemessagetx( tail ); | ||
| 306 | } | ||
| 307 | 309 | ||
| 308 | static void | 310 | static void command_format(char *line) { |
| 309 | command_format(char *line) { | ||
| 310 | struct stat testexist; | 311 | struct stat testexist; |
| 311 | char * tildex = NULL; | 312 | char *tildex = NULL; |
| 312 | 313 | ||
| 313 | flushout(); | 314 | flushout(); |
| 314 | while( *line==' ') line++; | 315 | while (*line == ' ') |
| 315 | if(line) { | 316 | line++; |
| 316 | tildex = tilde_expand( line ); | 317 | if (line) { |
| 317 | if(tildex && !stat(tildex, &testexist )) | 318 | tildex = tilde_expand(line); |
| 319 | if (tildex && !stat(tildex, &testexist)) | ||
| 318 | loadformats(tildex); | 320 | loadformats(tildex); |
| 319 | else { | 321 | else { |
| 320 | #define BUFSIZE 4096 | 322 | #define BUFSIZE 4096 |
| 321 | char buf[BUFSIZE]; | 323 | char buf[BUFSIZE]; |
| 322 | snprintf( buf, BUFSIZE, "~/.vchat/sample-%s.fmt", line ); | 324 | snprintf(buf, BUFSIZE, "~/.vchat/sample-%s.fmt", line); |
| 323 | free(tildex); | 325 | free(tildex); |
| 324 | tildex = tilde_expand( line ); | 326 | tildex = tilde_expand(line); |
| 325 | if(tildex && !stat(tildex, &testexist )) | 327 | if (tildex && !stat(tildex, &testexist)) |
| 326 | loadformats(tildex); | 328 | loadformats(tildex); |
| 327 | } | 329 | } |
| 328 | writeout(" Sort of done. "); | 330 | writeout(" Sort of done. "); |
| @@ -334,106 +336,101 @@ command_format(char *line) { | |||
| 334 | } | 336 | } |
| 335 | 337 | ||
| 336 | /* handle a help request */ | 338 | /* handle a help request */ |
| 337 | static void | 339 | static void command_help(char *line) { |
| 338 | command_help (char *line) { | 340 | flushout(); |
| 339 | flushout( ); | 341 | while (*line == ' ') |
| 340 | while( *line==' ') line++; | 342 | line++; |
| 341 | if( *line ) { /* Get help on command */ | 343 | if (*line) { /* Get help on command */ |
| 342 | int i; | 344 | int i; |
| 343 | if( ( i = translatecommand( &line ) ) != COMMAND_NONE ) { | 345 | if ((i = translatecommand(&line)) != COMMAND_NONE) { |
| 344 | snprintf( tmpstr, TMPSTRSIZE, "Help on command: %s", commandtable[i].name); | 346 | snprintf(tmpstr, TMPSTRSIZE, "Help on command: %s", commandtable[i].name); |
| 345 | writeout( tmpstr ); | 347 | writeout(tmpstr); |
| 346 | writeout(" "); | 348 | writeout(" "); |
| 347 | if( commandtable[i].short_help && !commandtable[i].help ) | 349 | if (commandtable[i].short_help && !commandtable[i].help) |
| 348 | writeout(commandtable[i].short_help ); | 350 | writeout(commandtable[i].short_help); |
| 349 | line = commandtable[i].help; | 351 | line = commandtable[i].help; |
| 350 | if( line ) { | 352 | if (line) { |
| 351 | while( *line ) { | 353 | while (*line) { |
| 352 | char *tmp = tmpstr; | 354 | char *tmp = tmpstr; |
| 353 | while( *line && (*line != '\n') ) | 355 | while (*line && (*line != '\n')) |
| 354 | *tmp++ = *line++; | 356 | *tmp++ = *line++; |
| 355 | *tmp = '\0'; if( *line == '\n') line++; | 357 | *tmp = '\0'; |
| 356 | writeout ( tmpstr ); | 358 | if (*line == '\n') |
| 357 | } | 359 | line++; |
| 358 | } | 360 | writeout(tmpstr); |
| 359 | } else { | 361 | } |
| 360 | command_help( " " ); | ||
| 361 | } | ||
| 362 | } else { /* Get overall help */ | ||
| 363 | int i; | ||
| 364 | for( i = 0; commandtable[i].number != COMMAND_NONE; i++ ) { | ||
| 365 | if( commandtable[i].short_help ) | ||
| 366 | writeout( commandtable[i].short_help ); | ||
| 367 | } | 362 | } |
| 363 | } else { | ||
| 364 | command_help(" "); | ||
| 365 | } | ||
| 366 | } else { /* Get overall help */ | ||
| 367 | int i; | ||
| 368 | for (i = 0; commandtable[i].number != COMMAND_NONE; i++) { | ||
| 369 | if (commandtable[i].short_help) | ||
| 370 | writeout(commandtable[i].short_help); | ||
| 371 | } | ||
| 368 | } | 372 | } |
| 369 | showout(); | 373 | showout(); |
| 370 | } | 374 | } |
| 371 | 375 | ||
| 372 | /* handle an unknown command */ | 376 | /* handle an unknown command */ |
| 373 | static void | 377 | static void command_none(char *line) { |
| 374 | command_none(char *line) { | 378 | snprintf(tmpstr, TMPSTRSIZE, " Unknown client command: %s ", line); |
| 375 | snprintf(tmpstr, TMPSTRSIZE, " Unknown client command: %s ", line); | 379 | msgout(tmpstr); |
| 376 | msgout(tmpstr); | ||
| 377 | } | 380 | } |
| 378 | 381 | ||
| 379 | /* handle a "/flt " request */ | 382 | /* handle a "/flt " request */ |
| 380 | static void | 383 | static void command_flt(char *tail) { |
| 381 | command_flt(char *tail){ | ||
| 382 | char colour; | 384 | char colour; |
| 383 | while(*tail==' ') tail++; | 385 | while (*tail == ' ') |
| 386 | tail++; | ||
| 384 | colour = *tail++; | 387 | colour = *tail++; |
| 385 | while( colour && *tail == ' ') tail++; | 388 | while (colour && *tail == ' ') |
| 386 | if( colour && *tail) { | 389 | tail++; |
| 387 | addfilter( colour, tail); | 390 | if (colour && *tail) { |
| 391 | addfilter(colour, tail); | ||
| 388 | } | 392 | } |
| 389 | } | 393 | } |
| 390 | 394 | ||
| 391 | /* handle a "/clflt " request */ | 395 | /* handle a "/clflt " request */ |
| 392 | static void | 396 | static void command_clflt(char *tail) { |
| 393 | command_clflt (char *tail) { | 397 | while (*tail == ' ') |
| 394 | while( *tail == ' ') tail++; | 398 | tail++; |
| 395 | clearfilters( *tail ); | 399 | clearfilters(*tail); |
| 396 | } | 400 | } |
| 397 | 401 | ||
| 398 | /* handle a "/rmflt " request */ | 402 | /* handle a "/rmflt " request */ |
| 399 | static void | 403 | static void command_rmflt(char *tail) { |
| 400 | command_rmflt (char *tail) { | 404 | while (*tail == ' ') |
| 401 | while( *tail == ' ') tail++; | 405 | tail++; |
| 402 | removefilter( tail ); | 406 | removefilter(tail); |
| 403 | } | 407 | } |
| 404 | 408 | ||
| 405 | /* list filters */ | 409 | /* list filters */ |
| 406 | static void | 410 | static void command_lsflt(char *tail) { |
| 407 | command_lsflt (char *tail) { | 411 | (void)tail; |
| 408 | listfilters(); | 412 | listfilters(); |
| 409 | } | 413 | } |
| 410 | 414 | ||
| 411 | /* handle a "/me " action */ | 415 | /* handle a "/me " action */ |
| 412 | static void | 416 | static void command_action(char *tail) { doaction(tail); } |
| 413 | command_action(char *tail) | ||
| 414 | { | ||
| 415 | doaction( tail); | ||
| 416 | } | ||
| 417 | 417 | ||
| 418 | /* handle a "/reconnect" request */ | 418 | /* handle a "/reconnect" request */ |
| 419 | static void | 419 | static void command_reconnect(char *tail) { |
| 420 | command_reconnect(char *tail) | 420 | (void)tail; |
| 421 | { | ||
| 422 | status = 0; | 421 | status = 0; |
| 423 | wantreconnect = 1; | 422 | wantreconnect = 1; |
| 424 | ownquit = 0; | 423 | ownquit = 0; |
| 425 | } | 424 | } |
| 426 | 425 | ||
| 427 | /* handle a "/quit " exit */ | 426 | /* handle a "/quit " exit */ |
| 428 | static void | 427 | static void command_quit(char *tail) { |
| 429 | command_quit(char *tail) | ||
| 430 | { | ||
| 431 | /* send users message to server */ | 428 | /* send users message to server */ |
| 432 | snprintf (tmpstr, TMPSTRSIZE, ".x %s", tail); | 429 | snprintf(tmpstr, TMPSTRSIZE, ".x %s", tail); |
| 433 | networkoutput (tmpstr); | 430 | vc_sendmessage(tmpstr); |
| 434 | 431 | ||
| 435 | /* show action in channel window */ | 432 | /* show action in channel window */ |
| 436 | writechan (tmpstr); | 433 | writechan(tmpstr); |
| 437 | 434 | ||
| 438 | /* Inform vchat-client, that the closing connection | 435 | /* Inform vchat-client, that the closing connection |
| 439 | following is intended */ | 436 | following is intended */ |
| @@ -442,41 +439,35 @@ command_quit(char *tail) | |||
| 442 | } | 439 | } |
| 443 | 440 | ||
| 444 | /* print out version */ | 441 | /* print out version */ |
| 445 | void | 442 | void command_version(char *tail) { |
| 446 | command_version(char *tail) | 443 | (void)tail; |
| 447 | { | ||
| 448 | /* output internal versions of all modules */ | 444 | /* output internal versions of all modules */ |
| 449 | flushout(); | 445 | flushout(); |
| 450 | writeout (vchat_cl_version); | 446 | writeout(vchat_cl_version); |
| 451 | writeout (vchat_ui_version); | 447 | writeout(vchat_ui_version); |
| 452 | writeout (vchat_io_version); | 448 | writeout(vchat_io_version); |
| 453 | writeout (vchat_us_version); | 449 | writeout(vchat_us_version); |
| 454 | writeout (vchat_cm_version); | 450 | writeout(vchat_cm_version); |
| 455 | writeout (vchat_ssl_version); | 451 | writeout(vchat_tls_version); |
| 456 | writeout (vchat_ssl_version_external); | 452 | writeout(vchat_tls_version_external()); |
| 457 | showout(); | 453 | showout(); |
| 458 | } | 454 | } |
| 459 | 455 | ||
| 460 | /* start or end a query */ | 456 | /* start or end a query */ |
| 461 | void | 457 | void command_query(char *tail) { |
| 462 | command_query(char *tail) | ||
| 463 | { | ||
| 464 | char *msg; | 458 | char *msg; |
| 465 | while( *tail == ' ') tail++; | 459 | while (*tail == ' ') |
| 460 | tail++; | ||
| 466 | 461 | ||
| 467 | // Check, if a message is to be sent in first query | 462 | // Check, if a message is to be sent in first query |
| 468 | // Note: this is safe, since readline chops trailing spaces | 463 | // Note: this is safe, since readline chops trailing spaces |
| 469 | if((msg = strchr(tail, ' '))) { | 464 | if ((msg = strchr(tail, ' '))) { |
| 470 | privatemessagetx( tail ); | 465 | privatemessagetx(tail); |
| 471 | *msg = 0; | 466 | *msg = 0; |
| 472 | } | 467 | } |
| 473 | 468 | ||
| 474 | // Do the ui stuff for query | 469 | // Do the ui stuff for query |
| 475 | handlequery( tail ); | 470 | handlequery(tail); |
| 476 | } | 471 | } |
| 477 | 472 | ||
| 478 | void | 473 | void command_dict(char *tail) { ul_add_to_dict(tail); } |
| 479 | command_dict(char *tail) | ||
| 480 | { | ||
| 481 | ul_add_to_dict(tail); | ||
| 482 | } | ||
diff --git a/vchat-config.h b/vchat-config.h index f7123d7..24365dc 100755..100644 --- a/vchat-config.h +++ b/vchat-config.h | |||
| @@ -29,19 +29,21 @@ extern unsigned int hscroll; | |||
| 29 | static volatile configoption configoptions[] = { | 29 | static volatile configoption configoptions[] = { |
| 30 | /* config-option type name in file default value value localvar */ | 30 | /* config-option type name in file default value value localvar */ |
| 31 | {CF_NICK, CO_STR, "nick", NULL, NULL, { NULL } }, | 31 | {CF_NICK, CO_STR, "nick", NULL, NULL, { NULL } }, |
| 32 | {CF_FROM, CO_STR, "from", "vc-alpha-0.19", NULL, { NULL } }, | 32 | {CF_FROM, CO_STR, "from", "vc-alpha-0.20", NULL, { NULL } }, |
| 33 | {CF_SERVERHOST, CO_STR, "host", "localhost", NULL, { NULL } }, | 33 | {CF_SERVERHOST, CO_STR, "host", "localhost", NULL, { NULL } }, |
| 34 | {CF_SERVERPORT, CO_STR, "port", "2325", NULL, { NULL } }, | 34 | {CF_SERVERPORT, CO_STR, "port", "2325", NULL, { NULL } }, |
| 35 | {CF_CIPHERSUITE, CO_STR, "ciphers", NULL, NULL, { NULL } }, | 35 | {CF_CIPHERSUITE, CO_STR, "ciphers", NULL, NULL, { NULL } }, |
| 36 | {CF_CONFIGFILE, CO_STR, "conffile", "~/.vchat/config", NULL, { NULL } }, | 36 | {CF_CONFIGFILE, CO_STR, "conffile", "~/.vchat/config", NULL, { NULL } }, |
| 37 | {CF_CERTFILE, CO_STR, "certfile", "~/.vchat/cert", NULL, { NULL } }, | 37 | {CF_CERTFILE, CO_STR, "certfile", "~/.vchat/cert", NULL, { NULL } }, |
| 38 | {CF_KEYFILE, CO_STR, "keyfile", "~/.vchat/key", NULL, { NULL } }, | 38 | {CF_KEYFILE, CO_STR, "keyfile", "~/.vchat/key", NULL, { NULL } }, |
| 39 | {CF_CAFILE, CO_STR, "cafile", "~/.vchat/ca", NULL, { NULL } }, | ||
| 39 | {CF_FORMFILE, CO_STR, "formatfile", "~/.vchat/formats", NULL, { NULL } }, | 40 | {CF_FORMFILE, CO_STR, "formatfile", "~/.vchat/formats", NULL, { NULL } }, |
| 40 | {CF_LOGINSCRIPT, CO_STR, "loginscript","~/.vchat/loginscript", NULL, { NULL } }, | 41 | {CF_LOGINSCRIPT, CO_STR, "loginscript","~/.vchat/loginscript", NULL, { NULL } }, |
| 41 | {CF_FINGERPRINT, CO_STR, "fingerprint","~/.vchat/fingerprint", NULL, { NULL } }, | 42 | {CF_FINGERPRINT, CO_STR, "fingerprint","~/.vchat/fingerprint", NULL, { NULL } }, |
| 42 | {CF_PINFINGER, CO_INT, "pinfinger", (char *) 0, (char *)-1, { NULL } }, | 43 | {CF_PINFINGER, CO_INT, "pinfinger", (char *) 0, (char *)-1, { NULL } }, |
| 43 | {CF_ENCODING, CO_STR, "encoding", NULL, NULL, { .pstr = &encoding }}, | 44 | {CF_ENCODING, CO_STR, "encoding", NULL, NULL, { .pstr = &encoding }}, |
| 44 | {CF_USESSL, CO_INT, "usessl", (char *) 1, (char *)-1, { NULL } }, | 45 | {CF_USESSL, CO_INT, "usessl", (char *) 1, (char *)-1, { NULL } }, |
| 46 | {CF_TLSLIB, CO_STR, "tlslib", "mbedtls", NULL, { NULL } }, | ||
| 45 | {CF_IGNSSL, CO_INT, "ignssl", (char *) 0, (char *)-1, { NULL } }, | 47 | {CF_IGNSSL, CO_INT, "ignssl", (char *) 0, (char *)-1, { NULL } }, |
| 46 | {CF_VERIFYSSL, CO_INT, "verifyssl", (char *) 2, (char *)-1, { NULL } }, | 48 | {CF_VERIFYSSL, CO_INT, "verifyssl", (char *) 2, (char *)-1, { NULL } }, |
| 47 | {CF_USECERT, CO_INT, "usecert", (char *) 1, (char *)-1, { NULL } }, | 49 | {CF_USECERT, CO_INT, "usecert", (char *) 1, (char *)-1, { NULL } }, |
| @@ -49,6 +51,7 @@ static volatile configoption configoptions[] = { | |||
| 49 | {CF_USETOPIC, CO_INT, "usetopicbar",(char *) 1, (char *)-1, { NULL } }, | 51 | {CF_USETOPIC, CO_INT, "usetopicbar",(char *) 1, (char *)-1, { NULL } }, |
| 50 | {CF_PRIVHEIGHT, CO_INT, "messages", (char *) 0, (char *)-1, { NULL } }, | 52 | {CF_PRIVHEIGHT, CO_INT, "messages", (char *) 0, (char *)-1, { NULL } }, |
| 51 | {CF_PRIVCOLLAPS, CO_INT, "privcollaps",(char *) 0, (char *)-1, { NULL } }, | 53 | {CF_PRIVCOLLAPS, CO_INT, "privcollaps",(char *) 0, (char *)-1, { NULL } }, |
| 54 | {CF_INVWINBAR, CO_INT, "invwinbar", (char *) 0, (char *)-1, { NULL } }, | ||
| 52 | {CF_HSCROLL, CO_INT, "hscroll", (char *)15, (char *)-1, { .pint = &hscroll } }, | 55 | {CF_HSCROLL, CO_INT, "hscroll", (char *)15, (char *)-1, { .pint = &hscroll } }, |
| 53 | {CF_CHANNEL, CO_INT, "channel", (char *) 0, (char *)-1, { NULL } }, | 56 | {CF_CHANNEL, CO_INT, "channel", (char *) 0, (char *)-1, { NULL } }, |
| 54 | {CF_SCROLLBPRIV, CO_INT, "privscrollb",(char *) 2048, (char *)-1, { NULL } }, | 57 | {CF_SCROLLBPRIV, CO_INT, "privscrollb",(char *) 2048, (char *)-1, { NULL } }, |
diff --git a/vchat-connection.c b/vchat-connection.c new file mode 100644 index 0000000..dea69d0 --- /dev/null +++ b/vchat-connection.c | |||
| @@ -0,0 +1,350 @@ | |||
| 1 | /* | ||
| 2 | * vchat-client - alpha version | ||
| 3 | * vchat-connection.c - handling of server connection and tls library dispatch | ||
| 4 | * | ||
| 5 | * Copyright (C) 2022 Dirk Engling <erdgeist@erdgeist.org> | ||
| 6 | * | ||
| 7 | * This program is free software. It can be redistributed and/or modified, | ||
| 8 | * provided that this copyright notice is kept intact. This program is | ||
| 9 | * distributed in the hope that it will be useful, but without any warranty; | ||
| 10 | * without even the implied warranty of merchantability or fitness for a | ||
| 11 | * particular purpose. In no event shall the copyright holder be liable for | ||
| 12 | * any direct, indirect, incidental or special damages arising in any way out | ||
| 13 | * of the use of this software. | ||
| 14 | * | ||
| 15 | */ | ||
| 16 | |||
| 17 | #include <errno.h> | ||
| 18 | #include <netdb.h> | ||
| 19 | #include <netinet/in.h> | ||
| 20 | #include <stddef.h> | ||
| 21 | #include <stdio.h> | ||
| 22 | #include <stdlib.h> | ||
| 23 | #include <string.h> | ||
| 24 | #include <sys/socket.h> | ||
| 25 | #include <sys/types.h> | ||
| 26 | #include <unistd.h> | ||
| 27 | |||
| 28 | /* For tilde_expand */ | ||
| 29 | #include <readline/readline.h> | ||
| 30 | |||
| 31 | #include "vchat-connection.h" | ||
| 32 | #include "vchat-tls.h" | ||
| 33 | #include "vchat.h" | ||
| 34 | |||
| 35 | static int serverfd = -1; | ||
| 36 | unsigned int want_tcp_keepalive = 0; | ||
| 37 | |||
| 38 | enum { TLS_ENGINE_UNSET, TLS_ENGINE_OPENSSL, TLS_ENGINE_MBEDTLS }; | ||
| 39 | static int _engine = TLS_ENGINE_UNSET; | ||
| 40 | |||
| 41 | #define STAGING_SIZE 16384 | ||
| 42 | #define RECEIVEBUF_SIZE 4096 | ||
| 43 | |||
| 44 | /* Generic tcp connector, blocking */ | ||
| 45 | static int connect_tcp_socket(const char *server, const char *port) { | ||
| 46 | struct addrinfo hints, *res, *res0; | ||
| 47 | int s, error; | ||
| 48 | |||
| 49 | memset(&hints, 0, sizeof(hints)); | ||
| 50 | hints.ai_family = PF_UNSPEC; | ||
| 51 | hints.ai_socktype = SOCK_STREAM; | ||
| 52 | error = getaddrinfo(server, port, &hints, &res0); | ||
| 53 | if (error) | ||
| 54 | return -1; | ||
| 55 | s = -1; | ||
| 56 | for (res = res0; res; res = res->ai_next) { | ||
| 57 | s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); | ||
| 58 | if (s < 0) | ||
| 59 | continue; | ||
| 60 | if (connect(s, res->ai_addr, res->ai_addrlen) < 0) { | ||
| 61 | close(s); | ||
| 62 | s = -1; | ||
| 63 | continue; | ||
| 64 | } | ||
| 65 | break; /* okay we got one */ | ||
| 66 | } | ||
| 67 | freeaddrinfo(res0); | ||
| 68 | |||
| 69 | if (want_tcp_keepalive) { | ||
| 70 | int one = 1; | ||
| 71 | setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof(one)); | ||
| 72 | } | ||
| 73 | return s; | ||
| 74 | } | ||
| 75 | |||
| 76 | /* Return a tilde expanded path in a malloced buffer or NULL */ | ||
| 77 | static char *get_tilde_expanded(confopt opt) { | ||
| 78 | char *str = getstroption(opt); | ||
| 79 | if (!str) | ||
| 80 | return str; | ||
| 81 | if (str[0] == '~') | ||
| 82 | return tilde_expand(str); | ||
| 83 | return strdup(str); | ||
| 84 | } | ||
| 85 | |||
| 86 | /* connects to server */ | ||
| 87 | int vc_connect(const char *server, const char *port) { | ||
| 88 | /* vchat connection x509 store */ | ||
| 89 | vc_x509store_t vc_store; | ||
| 90 | |||
| 91 | /* pointer to tilde-expanded certificate/keyfile-names */ | ||
| 92 | char *certfile, *cafile; | ||
| 93 | int result = -1, want_openssl = !strcmp(getstroption(CF_TLSLIB), "openssl"); | ||
| 94 | |||
| 95 | /* Connect to the server */ | ||
| 96 | serverfd = connect_tcp_socket(server, port); | ||
| 97 | if (serverfd < 0) { | ||
| 98 | /* inform user */ | ||
| 99 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_CANTCONNECT), server, port); | ||
| 100 | writechan(tmpstr); | ||
| 101 | return -1; | ||
| 102 | } | ||
| 103 | |||
| 104 | if (!getintoption(CF_USESSL)) | ||
| 105 | return 0; | ||
| 106 | |||
| 107 | #ifdef TLS_LIB_OPENSSL | ||
| 108 | _engine = TLS_ENGINE_OPENSSL; | ||
| 109 | #endif | ||
| 110 | #ifdef TLS_LIB_MBEDTLS | ||
| 111 | /* Make mbedtls default unless mbedtls is configured */ | ||
| 112 | if (!want_openssl || _engine == TLS_ENGINE_UNSET) | ||
| 113 | _engine = TLS_ENGINE_MBEDTLS; | ||
| 114 | #endif | ||
| 115 | |||
| 116 | if (_engine == TLS_ENGINE_UNSET) { | ||
| 117 | writecf(FS_ERR, "Error: tls requested but no tls engine compiled in."); | ||
| 118 | return -1; | ||
| 119 | } | ||
| 120 | |||
| 121 | if (want_openssl && _engine == TLS_ENGINE_MBEDTLS) | ||
| 122 | writecf(FS_SERV, "Warning: tls engine openssl requested but openssl engine " | ||
| 123 | "not compiled in. Using mbedtls"); | ||
| 124 | |||
| 125 | if (!want_openssl && _engine == TLS_ENGINE_OPENSSL) | ||
| 126 | writecf(FS_SERV, "Warning: tls engine mbedtls requested but mbedts engine " | ||
| 127 | "not compiled in. Using openssl"); | ||
| 128 | |||
| 129 | /* If SSL is requested, get our ssl-BIO running */ | ||
| 130 | #ifdef TLS_LIB_OPENSSL | ||
| 131 | if (_engine == TLS_ENGINE_OPENSSL) | ||
| 132 | vc_openssl_init_x509store(&vc_store); | ||
| 133 | #endif | ||
| 134 | #ifdef TLS_LIB_MBEDTLS | ||
| 135 | if (_engine == TLS_ENGINE_MBEDTLS) | ||
| 136 | vc_mbedtls_init_x509store(&vc_store); | ||
| 137 | #endif | ||
| 138 | |||
| 139 | /* get name of certificate file */ | ||
| 140 | certfile = get_tilde_expanded(CF_CERTFILE); | ||
| 141 | /* do we have a certificate file? */ | ||
| 142 | if (certfile) { | ||
| 143 | /* get name of key file */ | ||
| 144 | char *keyfile = get_tilde_expanded(CF_KEYFILE); | ||
| 145 | |||
| 146 | vc_x509store_setcertfile(&vc_store, certfile); | ||
| 147 | vc_x509store_set_pkeycb(&vc_store, (vc_askpass_cb_t)passprompt); | ||
| 148 | |||
| 149 | /* if we don't have a key file, the key may be in the cert file */ | ||
| 150 | vc_x509store_setkeyfile(&vc_store, keyfile ? keyfile : certfile); | ||
| 151 | |||
| 152 | free(keyfile); | ||
| 153 | free(certfile); | ||
| 154 | } | ||
| 155 | |||
| 156 | /* get name of ca file */ | ||
| 157 | cafile = get_tilde_expanded(CF_CAFILE); | ||
| 158 | if (cafile && !access(cafile, F_OK)) | ||
| 159 | vc_x509store_setcafile(&vc_store, cafile); | ||
| 160 | free(cafile); | ||
| 161 | |||
| 162 | /* upgrade our plain BIO to ssl */ | ||
| 163 | #ifdef TLS_LIB_OPENSSL | ||
| 164 | if (_engine == TLS_ENGINE_OPENSSL) | ||
| 165 | result = vc_openssl_connect(serverfd, &vc_store); | ||
| 166 | #endif | ||
| 167 | #ifdef TLS_LIB_MBEDTLS | ||
| 168 | if (_engine == TLS_ENGINE_MBEDTLS) | ||
| 169 | result = vc_mbedtls_connect(serverfd, &vc_store); | ||
| 170 | #endif | ||
| 171 | vc_cleanup_x509store(&vc_store); | ||
| 172 | |||
| 173 | if (result) { | ||
| 174 | close(serverfd); | ||
| 175 | serverfd = -1; | ||
| 176 | errno = EIO; | ||
| 177 | #ifdef TLS_LIB_OPENSSL | ||
| 178 | if (_engine == TLS_ENGINE_OPENSSL) | ||
| 179 | vc_openssl_cleanup(); | ||
| 180 | #endif | ||
| 181 | #ifdef TLS_LIB_MBEDTLS | ||
| 182 | if (_engine == TLS_ENGINE_MBEDTLS) | ||
| 183 | vc_mbedtls_cleanup(); | ||
| 184 | #endif | ||
| 185 | |||
| 186 | _engine = TLS_ENGINE_UNSET; | ||
| 187 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_CANTCONNECT), server, port); | ||
| 188 | writechan(tmpstr); | ||
| 189 | return -1; | ||
| 190 | } | ||
| 191 | |||
| 192 | /* inform user */ | ||
| 193 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_CONNECTED), server, port); | ||
| 194 | writechan(tmpstr); | ||
| 195 | |||
| 196 | #ifdef DEBUG | ||
| 197 | dumpfile = fopen("dumpfile", "a"); | ||
| 198 | #endif | ||
| 199 | |||
| 200 | /* if we didn't fail until now, we've got a connection. */ | ||
| 201 | return 0; | ||
| 202 | } | ||
| 203 | |||
| 204 | /* Poll for activity on the socket or stdin */ | ||
| 205 | int vc_poll(int timeout_seconds) { | ||
| 206 | fd_set readfds; | ||
| 207 | FD_ZERO(&readfds); | ||
| 208 | FD_SET(0, &readfds); | ||
| 209 | if (serverfd != -1) | ||
| 210 | FD_SET(serverfd, &readfds); | ||
| 211 | struct timeval tv = {timeout_seconds, 0}; | ||
| 212 | int result = select(serverfd + 2, &readfds, NULL, NULL, &tv); | ||
| 213 | if (result <= 0) | ||
| 214 | return result; | ||
| 215 | result = FD_ISSET(0, &readfds) ? 1 : 0; | ||
| 216 | if (serverfd != -1) | ||
| 217 | result += FD_ISSET(serverfd, &readfds) ? 2 : 0; | ||
| 218 | return result; | ||
| 219 | } | ||
| 220 | |||
| 221 | /* disconnect from server */ | ||
| 222 | void vc_disconnect() { | ||
| 223 | if (serverfd > 0) { | ||
| 224 | close(serverfd); | ||
| 225 | serverfd = -1; | ||
| 226 | } | ||
| 227 | #ifdef TLS_LIB_OPENSSL | ||
| 228 | if (_engine == TLS_ENGINE_OPENSSL) | ||
| 229 | vc_openssl_cleanup(); | ||
| 230 | #endif | ||
| 231 | #ifdef TLS_LIB_MBEDTLS | ||
| 232 | if (_engine == TLS_ENGINE_MBEDTLS) | ||
| 233 | vc_mbedtls_cleanup(); | ||
| 234 | #endif | ||
| 235 | |||
| 236 | _engine = TLS_ENGINE_UNSET; | ||
| 237 | loggedin = 0; | ||
| 238 | } | ||
| 239 | |||
| 240 | void vc_sendmessage(const char *msg) { | ||
| 241 | static char staging[STAGING_SIZE]; | ||
| 242 | size_t sent = 0, len = snprintf(staging, sizeof(staging), "%s\r\n", msg); | ||
| 243 | #ifdef DEBUG | ||
| 244 | /* debugging? log network output! */ | ||
| 245 | fprintf(dumpfile, ">| (%zd) %s\n", len - 2, msg); | ||
| 246 | #endif | ||
| 247 | |||
| 248 | if (getintoption(CF_USESSL)) { | ||
| 249 | #ifdef TLS_LIB_OPENSSL | ||
| 250 | if (_engine == TLS_ENGINE_OPENSSL) | ||
| 251 | sent = vc_openssl_sendmessage(staging, len); | ||
| 252 | #endif | ||
| 253 | #ifdef TLS_LIB_MBEDTLS | ||
| 254 | if (_engine == TLS_ENGINE_MBEDTLS) | ||
| 255 | sent = vc_mbedtls_sendmessage(staging, len); | ||
| 256 | #endif | ||
| 257 | } else | ||
| 258 | sent = write(serverfd, staging, len); | ||
| 259 | if (sent != len) | ||
| 260 | writecf(FS_ERR, "Message sending fuzzy."); | ||
| 261 | } | ||
| 262 | |||
| 263 | /* get data from servers connection */ | ||
| 264 | int vc_receive(void) { | ||
| 265 | /* offset in buffer (for linebreaks at packet borders) */ | ||
| 266 | static char buf[RECEIVEBUF_SIZE]; | ||
| 267 | static size_t buf_fill; | ||
| 268 | char *endmsg; | ||
| 269 | size_t freebytes = sizeof(buf) - buf_fill; | ||
| 270 | ssize_t bytes = 0; | ||
| 271 | |||
| 272 | if (!getintoption(CF_USESSL)) | ||
| 273 | bytes = read(serverfd, buf + buf_fill, freebytes); | ||
| 274 | else | ||
| 275 | #ifdef TLS_LIB_OPENSSL | ||
| 276 | if (_engine == TLS_ENGINE_OPENSSL) | ||
| 277 | bytes = vc_openssl_receivemessage(buf + buf_fill, freebytes); | ||
| 278 | #endif | ||
| 279 | #ifdef TLS_LIB_MBEDTLS | ||
| 280 | if (_engine == TLS_ENGINE_MBEDTLS) | ||
| 281 | bytes = vc_mbedtls_receivemessage(buf + buf_fill, freebytes); | ||
| 282 | #endif | ||
| 283 | |||
| 284 | /* Our tls functions may require retries with handshakes etc, this is | ||
| 285 | * signalled by -2 */ | ||
| 286 | if (bytes == -2) | ||
| 287 | return 0; | ||
| 288 | |||
| 289 | /* Error on the socket read? raise error message, bail out */ | ||
| 290 | if (bytes == -1) { | ||
| 291 | snprintf(tmpstr, TMPSTRSIZE, "Receive fails, %s.", strerror(errno)); | ||
| 292 | snprintf(errstr, ERRSTRSIZE, "Receive fails, %s.\n", strerror(errno)); | ||
| 293 | writecf(FS_ERR, tmpstr); | ||
| 294 | return -1; | ||
| 295 | } | ||
| 296 | |||
| 297 | /* end of file from server? */ | ||
| 298 | if (bytes == 0) { | ||
| 299 | /* inform user, bail out */ | ||
| 300 | writecf(FS_SERV, "* EOF from server."); | ||
| 301 | snprintf(errstr, ERRSTRSIZE, "* EOF from server.\n"); | ||
| 302 | return -1; | ||
| 303 | } | ||
| 304 | |||
| 305 | buf_fill += bytes; | ||
| 306 | |||
| 307 | /* as long as there are lines .. */ | ||
| 308 | while ((endmsg = memchr(buf, '\n', buf_fill)) != NULL) { | ||
| 309 | if (endmsg > buf) { | ||
| 310 | /* Zero terminate message, optionally chomp CR */ | ||
| 311 | endmsg[0] = 0; | ||
| 312 | if (endmsg[-1] == '\r') | ||
| 313 | endmsg[-1] = 0; | ||
| 314 | /* If terminating and chomping left us with a message, give it to line | ||
| 315 | * handler */ | ||
| 316 | if (buf[0]) { | ||
| 317 | #ifdef DEBUG | ||
| 318 | /* debugging? log network input! */ | ||
| 319 | fprintf(stderr, "<| %s\n", buf); | ||
| 320 | #endif | ||
| 321 | protocol_parsemsg(buf); | ||
| 322 | } | ||
| 323 | } | ||
| 324 | buf_fill -= 1 + endmsg - buf; | ||
| 325 | memmove(buf, endmsg + 1, buf_fill); | ||
| 326 | } | ||
| 327 | return 0; | ||
| 328 | } | ||
| 329 | |||
| 330 | const char *vchat_tls_version_external() { | ||
| 331 | #ifdef TLS_LIB_OPENSSL | ||
| 332 | char *openssl_version = vc_openssl_version(); | ||
| 333 | #else | ||
| 334 | char *openssl_version = strdup("not installed"); | ||
| 335 | #endif | ||
| 336 | #ifdef TLS_LIB_MBEDTLS | ||
| 337 | char *mbedtls_version = vc_mbedtls_version(); | ||
| 338 | #else | ||
| 339 | char *mbedtls_version = strdup("not installed"); | ||
| 340 | #endif | ||
| 341 | |||
| 342 | snprintf(tmpstr, TMPSTRSIZE, | ||
| 343 | "Module plain v0.1\nModule openssl version: %s\nModule mbedtls " | ||
| 344 | "version: %s", | ||
| 345 | openssl_version, mbedtls_version); | ||
| 346 | |||
| 347 | free(openssl_version); | ||
| 348 | free(mbedtls_version); | ||
| 349 | return tmpstr; | ||
| 350 | } | ||
diff --git a/vchat-connection.h b/vchat-connection.h new file mode 100644 index 0000000..84294f6 --- /dev/null +++ b/vchat-connection.h | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <stdint.h> | ||
| 4 | |||
| 5 | int vc_connect(const char *host, const char *port); | ||
| 6 | void vc_sendmessage(const char *message); | ||
| 7 | int vc_receive(); | ||
| 8 | int vc_poll(int); | ||
| 9 | void vc_disconnect(); | ||
| 10 | |||
| 11 | const char *vchat_tls_version_external(); | ||
diff --git a/vchat-help.h b/vchat-help.h index a7964fd..a7964fd 100755..100644 --- a/vchat-help.h +++ b/vchat-help.h | |||
diff --git a/vchat-howto b/vchat-howto index 27f8e14..27f8e14 100755..100644 --- a/vchat-howto +++ b/vchat-howto | |||
diff --git a/vchat-keygen b/vchat-keygen index 91fcbba..4163838 100755 --- a/vchat-keygen +++ b/vchat-keygen | |||
| @@ -29,7 +29,8 @@ if [ ! -e $KEYBASE.key ]; then | |||
| 29 | echo "vchat-keygen: generating RSA key $KEYBASE.key" | 29 | echo "vchat-keygen: generating RSA key $KEYBASE.key" |
| 30 | echo "vchat-keygen: please set passphrase for local security" | 30 | echo "vchat-keygen: please set passphrase for local security" |
| 31 | umask 0077 | 31 | umask 0077 |
| 32 | openssl genrsa -des3 -out $KEYBASE.key 4096 | 32 | openssl ecparam -genkey -name secp384r1 | \ |
| 33 | openssl ec -out $KEYBASE.key -aes256 | ||
| 33 | else | 34 | else |
| 34 | echo "vchat-keygen: private key $KEYBASE.key exists" | 35 | echo "vchat-keygen: private key $KEYBASE.key exists" |
| 35 | fi | 36 | fi |
| @@ -40,11 +41,11 @@ fi | |||
| 40 | echo "vchat-keygen: generating config-file for self-signing $KEYBASE.ca.keyconf" | 41 | echo "vchat-keygen: generating config-file for self-signing $KEYBASE.ca.keyconf" |
| 41 | cat >$KEYBASE.ca.keyconf <<EOT | 42 | cat >$KEYBASE.ca.keyconf <<EOT |
| 42 | [ req ] | 43 | [ req ] |
| 43 | default_bits = 4096 | ||
| 44 | default_keyfile = user.key | 44 | default_keyfile = user.key |
| 45 | distinguished_name = req_distinguished_name | 45 | distinguished_name = req_distinguished_name |
| 46 | string_mask = nombstr | 46 | string_mask = nombstr |
| 47 | req_extensions = v3_req | 47 | req_extensions = v3_req |
| 48 | default_md = sha384 | ||
| 48 | [ req_distinguished_name ] | 49 | [ req_distinguished_name ] |
| 49 | commonName = Name | 50 | commonName = Name |
| 50 | commonName_max = 64 | 51 | commonName_max = 64 |
| @@ -57,7 +58,7 @@ EOT | |||
| 57 | fi | 58 | fi |
| 58 | echo "vchat-keygen: generating Certificate Signing Request $KEYBASE.csr" | 59 | echo "vchat-keygen: generating Certificate Signing Request $KEYBASE.csr" |
| 59 | echo "vchat-keygen: please enter your nickname at the 'Name []:' prompt" | 60 | echo "vchat-keygen: please enter your nickname at the 'Name []:' prompt" |
| 60 | openssl req -new -sha1 -config $KEYBASE.ca.keyconf -key $KEYBASE.key -out $KEYBASE.csr | 61 | openssl req -new -sha256 -config $KEYBASE.ca.keyconf -key $KEYBASE.key -out $KEYBASE.csr |
| 61 | echo "vchat-keygen: send this ($KEYBASE.csr) Certificate Signing Request to | 62 | echo "vchat-keygen: send this ($KEYBASE.csr) Certificate Signing Request to |
| 62 | vchat@vchat.berlin.ccc.de to get it signed by the vchat-CA. You will | 63 | vchat@vchat.berlin.ccc.de to get it signed by the vchat-CA. You will |
| 63 | receive your signed Certificate shortly." | 64 | receive your signed Certificate shortly." |
diff --git a/vchat-messages.h b/vchat-messages.h index 0902e88..0902e88 100755..100644 --- a/vchat-messages.h +++ b/vchat-messages.h | |||
diff --git a/vchat-protocol.c b/vchat-protocol.c index f1b71a1..c5021e4 100755..100644 --- a/vchat-protocol.c +++ b/vchat-protocol.c | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * vchat-client - alpha version | 2 | * vchat-client - alpha version |
| 3 | * vchat-protocol.c - handling of server connection & messages | 3 | * vchat-protocol.c - handling of server messages |
| 4 | * | 4 | * |
| 5 | * Copyright (C) 2001 Andreas Kotes <count@flatline.de> | 5 | * Copyright (C) 2001 Andreas Kotes <count@flatline.de> |
| 6 | * | 6 | * |
| @@ -10,528 +10,367 @@ | |||
| 10 | * without even the implied warranty of merchantability or fitness for a | 10 | * without even the implied warranty of merchantability or fitness for a |
| 11 | * particular purpose. In no event shall the copyright holder be liable for | 11 | * particular purpose. In no event shall the copyright holder be liable for |
| 12 | * any direct, indirect, incidental or special damages arising in any way out | 12 | * any direct, indirect, incidental or special damages arising in any way out |
| 13 | * of the use of this software. | 13 | * of the use of this software. |
| 14 | * | 14 | * |
| 15 | */ | 15 | */ |
| 16 | 16 | ||
| 17 | /* general includes */ | 17 | /* general includes */ |
| 18 | #include <unistd.h> | ||
| 19 | #include <errno.h> | 18 | #include <errno.h> |
| 19 | #include <langinfo.h> | ||
| 20 | #include <locale.h> | ||
| 20 | #include <stdio.h> | 21 | #include <stdio.h> |
| 21 | #include <netdb.h> | 22 | #include <stdlib.h> |
| 22 | #include <string.h> | 23 | #include <string.h> |
| 23 | #include <sys/types.h> | 24 | |
| 24 | #include <sys/socket.h> | ||
| 25 | #include <netinet/in.h> | ||
| 26 | #include <readline/readline.h> | 25 | #include <readline/readline.h> |
| 27 | #include <locale.h> | ||
| 28 | #include <langinfo.h> | ||
| 29 | 26 | ||
| 30 | // TO BE GONE | 27 | #ifdef DEBUG |
| 31 | #include <openssl/bio.h> | 28 | FILE *dumpfile; |
| 29 | #endif | ||
| 32 | 30 | ||
| 33 | /* local includes */ | 31 | /* local includes */ |
| 34 | #include "vchat.h" | 32 | #include "vchat-connection.h" |
| 35 | #include "vchat-user.h" | 33 | #include "vchat-user.h" |
| 36 | #include "vchat-ssl.h" | 34 | #include "vchat.h" |
| 37 | 35 | ||
| 38 | /* version of this module */ | 36 | /* version of this module */ |
| 39 | const char *vchat_io_version = "vchat-protocol.c $Id$"; | 37 | const char *vchat_io_version = |
| 40 | 38 | "vchat-protocol.c $Id$"; | |
| 41 | /* externally used variables */ | ||
| 42 | int serverfd = -1; | ||
| 43 | |||
| 44 | /* locally global variables */ | ||
| 45 | /* our connection BIO */ | ||
| 46 | static BIO *server_conn = NULL; | ||
| 47 | 39 | ||
| 48 | /* declaration of local helper functions */ | 40 | /* declaration of local helper functions */ |
| 49 | static void usersignon (char *); | 41 | static void usersignon(char *); |
| 50 | static void usersignoff (char *); | 42 | static void usersignoff(char *); |
| 51 | static void usernickchange (char *); | 43 | static void usernickchange(char *); |
| 52 | static void userjoin (char *); | 44 | static void userjoin(char *); |
| 53 | static void userleave (char *); | 45 | static void userleave(char *); |
| 54 | static void receivenicks (char *message); | 46 | static void receivenicks(char *message); |
| 55 | static void justloggedin (char *message); | 47 | static void justloggedin(char *message); |
| 56 | static void nickerr (char *message); | 48 | static void nickerr(char *message); |
| 57 | static void login (char *message); | 49 | static void login(char *message); |
| 58 | static void anonlogin (char *message); | 50 | static void anonlogin(char *message); |
| 59 | static void topicinfo (char *message); | 51 | static void topicinfo(char *message); |
| 60 | static void pubaction (char *message); | 52 | static void pubaction(char *message); |
| 61 | static void pubthoughts (char *message); | 53 | static void pubthoughts(char *message); |
| 62 | static void serverlogin (char *message); | 54 | static void serverlogin(char *message); |
| 63 | static void idleprompt (char *message); | 55 | static void idleprompt(char *message); |
| 64 | static void topicchange (char *message); | 56 | static void topicchange(char *message); |
| 65 | static void pmnotsent (char *message); | 57 | static void pmnotsent(char *message); |
| 66 | 58 | ||
| 67 | /* declaration of server message array */ | 59 | /* declaration of server message array */ |
| 68 | #include "vchat-messages.h" | 60 | #include "vchat-messages.h" |
| 69 | 61 | ||
| 70 | /* status-variable from vchat-client.c | ||
| 71 | * eventloop is done as long as this is true */ | ||
| 72 | extern int status; | ||
| 73 | char *encoding; | 62 | char *encoding; |
| 74 | 63 | ||
| 75 | static int connect_socket( char *server, char *port ) { | ||
| 76 | struct addrinfo hints, *res, *res0; | ||
| 77 | int s, error; | ||
| 78 | |||
| 79 | memset(&hints, 0, sizeof(hints)); | ||
| 80 | hints.ai_family = PF_UNSPEC; | ||
| 81 | hints.ai_socktype = SOCK_STREAM; | ||
| 82 | error = getaddrinfo( server, port, &hints, &res0 ); | ||
| 83 | if (error) return -1; | ||
| 84 | s = -1; | ||
| 85 | for (res = res0; res; res = res->ai_next) { | ||
| 86 | s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); | ||
| 87 | if (s < 0) continue; | ||
| 88 | if (connect(s, res->ai_addr, res->ai_addrlen) < 0) { | ||
| 89 | close(s); | ||
| 90 | s = -1; | ||
| 91 | continue; | ||
| 92 | } | ||
| 93 | break; /* okay we got one */ | ||
| 94 | } | ||
| 95 | freeaddrinfo(res0); | ||
| 96 | |||
| 97 | if (want_tcp_keepalive) { /* global from vchat-client.c */ | ||
| 98 | int one=1; | ||
| 99 | setsockopt(s,SOL_SOCKET,SO_KEEPALIVE,&one,sizeof(one)); | ||
| 100 | } | ||
| 101 | return s; | ||
| 102 | } | ||
| 103 | |||
| 104 | /* connects to server */ | ||
| 105 | int | ||
| 106 | vcconnect (char *server, char *port) | ||
| 107 | { | ||
| 108 | /* used for tilde expansion of cert & key filenames */ | ||
| 109 | char *tildex = NULL; | ||
| 110 | |||
| 111 | /* vchat connection x509 store */ | ||
| 112 | vc_x509store_t *vc_store; | ||
| 113 | |||
| 114 | /* pointer to tilde-expanded certificate/keyfile-names */ | ||
| 115 | char *certfile = NULL, *keyfile = NULL; | ||
| 116 | |||
| 117 | /* Connect to the server */ | ||
| 118 | serverfd = connect_socket( server, port ); | ||
| 119 | if( serverfd < 0 ) { | ||
| 120 | /* inform user */ | ||
| 121 | snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_CANTCONNECT), server, port ); | ||
| 122 | writechan (tmpstr); | ||
| 123 | return -1; | ||
| 124 | } | ||
| 125 | /* Abstract server IO in openssls BIO */ | ||
| 126 | server_conn = BIO_new_socket( serverfd, 1 ); | ||
| 127 | |||
| 128 | /* If SSL is requested, get our ssl-BIO running */ | ||
| 129 | if( server_conn && getintoption(CF_USESSL) ) { | ||
| 130 | vc_store = vc_init_x509store(); | ||
| 131 | if( !vc_store ) { | ||
| 132 | snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_ERR), "Out of memory" ); | ||
| 133 | writechan (tmpstr); | ||
| 134 | return -1; | ||
| 135 | } | ||
| 136 | |||
| 137 | vc_x509store_setflags(vc_store, VC_X509S_SSL_VERIFY_PEER); | ||
| 138 | |||
| 139 | /* get name of certificate file */ | ||
| 140 | certfile = getstroption (CF_CERTFILE); | ||
| 141 | /* do we have a certificate file? */ | ||
| 142 | if (certfile) { | ||
| 143 | /* does the filename start with a tilde? expand it! */ | ||
| 144 | if (certfile[0] == '~') | ||
| 145 | tildex = tilde_expand (certfile); | ||
| 146 | else | ||
| 147 | tildex = certfile; | ||
| 148 | |||
| 149 | vc_x509store_setflags(vc_store, VC_X509S_USE_CERTIFICATE); | ||
| 150 | vc_x509store_setcertfile(vc_store, tildex); | ||
| 151 | |||
| 152 | /* get name of key file */ | ||
| 153 | keyfile = getstroption (CF_KEYFILE); | ||
| 154 | |||
| 155 | /* if we don't have a key file, the key may be in the cert file */ | ||
| 156 | if (!keyfile) | ||
| 157 | keyfile = certfile; | ||
| 158 | |||
| 159 | /* does the filename start with a tilde? expand it! */ | ||
| 160 | if (keyfile[0] == '~') | ||
| 161 | tildex = tilde_expand (keyfile); | ||
| 162 | else | ||
| 163 | tildex = keyfile; | ||
| 164 | |||
| 165 | vc_x509store_set_pkeycb(vc_store, (vc_askpass_cb_t)passprompt); | ||
| 166 | vc_x509store_setkeyfile(vc_store, tildex); | ||
| 167 | } | ||
| 168 | |||
| 169 | /* upgrade our plain BIO to ssl */ | ||
| 170 | if( vc_connect_ssl( &server_conn, vc_store ) ) { | ||
| 171 | BIO_free_all( server_conn ); | ||
| 172 | server_conn = NULL; | ||
| 173 | errno = EIO; | ||
| 174 | } | ||
| 175 | } | ||
| 176 | |||
| 177 | if( !server_conn ) { | ||
| 178 | /* inform user */ | ||
| 179 | snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_CANTCONNECT), server, port ); | ||
| 180 | writechan (tmpstr); | ||
| 181 | return -1; | ||
| 182 | } | ||
| 183 | |||
| 184 | /* inform user */ | ||
| 185 | snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_CONNECTED), server, port); | ||
| 186 | writechan (tmpstr); | ||
| 187 | |||
| 188 | /* if we didn't fail until now, we've got a connection. */ | ||
| 189 | return 0; | ||
| 190 | } | ||
| 191 | |||
| 192 | /* disconnect from server */ | ||
| 193 | void | ||
| 194 | vcdisconnect () { | ||
| 195 | BIO_free_all( server_conn ); | ||
| 196 | server_conn = 0; | ||
| 197 | if (serverfd>0) { | ||
| 198 | close(serverfd); | ||
| 199 | serverfd = -1; | ||
| 200 | } | ||
| 201 | } | ||
| 202 | |||
| 203 | /* handle a pm not sent error | 64 | /* handle a pm not sent error |
| 204 | * format: 412 %s */ | 65 | * format: 412 %s */ |
| 205 | static void | 66 | static void pmnotsent(char *message) { |
| 206 | pmnotsent (char *message) | 67 | while (*message && *message != ' ') |
| 207 | { | 68 | message++; |
| 208 | while(*message && *message!=' ') message++; | 69 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_ERR), message + 1); |
| 209 | snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_ERR),message+1); | 70 | writepriv(tmpstr, 0); |
| 210 | writepriv( tmpstr, 0); | ||
| 211 | |||
| 212 | } | 71 | } |
| 213 | 72 | ||
| 214 | /* parse and handle an action string | 73 | /* parse and handle an action string |
| 215 | * format: 118 %s %s | 74 | * format: 118 %s %s |
| 216 | * vars: %s nick | 75 | * vars: %s nick |
| 217 | * %s action */ | 76 | * %s action */ |
| 218 | static void | 77 | static void pubaction(char *message) { |
| 219 | pubaction (char *message) | ||
| 220 | { | ||
| 221 | char *nick = NULL, *action = NULL; | 78 | char *nick = NULL, *action = NULL; |
| 222 | nick = strchr (message, ' '); | 79 | nick = strchr(message, ' '); |
| 223 | nick[0] = '\0'; | 80 | nick[0] = '\0'; |
| 224 | nick++; | 81 | nick++; |
| 225 | 82 | ||
| 226 | action = strchr (nick, ' '); | 83 | action = strchr(nick, ' '); |
| 227 | action[0] = '\0'; | 84 | action[0] = '\0'; |
| 228 | action++; | 85 | action++; |
| 229 | 86 | ||
| 230 | ul_public_action(nick); | 87 | ul_public_action(nick); |
| 231 | snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_PUBACTION),nick,action); | 88 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_PUBACTION), nick, action); |
| 232 | writechan (tmpstr); | 89 | writechan(tmpstr); |
| 233 | } | 90 | } |
| 234 | 91 | ||
| 235 | /* parse and handle an thought string | 92 | /* parse and handle an thought string |
| 236 | * format: 124 %s %s | 93 | * format: 124 %s %s |
| 237 | * vars: %s nick | 94 | * vars: %s nick |
| 238 | * %s thought */ | 95 | * %s thought */ |
| 239 | static void | 96 | static void pubthoughts(char *message) { |
| 240 | pubthoughts (char *message) | ||
| 241 | { | ||
| 242 | char *nick = NULL, *thoughts = NULL; | 97 | char *nick = NULL, *thoughts = NULL; |
| 243 | nick = strchr (message, ' '); | 98 | nick = strchr(message, ' '); |
| 244 | nick[0] = '\0'; | 99 | nick[0] = '\0'; |
| 245 | nick++; | 100 | nick++; |
| 246 | 101 | ||
| 247 | thoughts = strchr (nick, ' '); | 102 | thoughts = strchr(nick, ' '); |
| 248 | thoughts[0] = '\0'; | 103 | thoughts[0] = '\0'; |
| 249 | thoughts++; | 104 | thoughts++; |
| 250 | 105 | ||
| 251 | ul_public_action(nick); | 106 | ul_public_action(nick); |
| 252 | snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_PUBTHOUGHT),nick,thoughts); | 107 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_PUBTHOUGHT), nick, thoughts); |
| 253 | writechan (tmpstr); | 108 | writechan(tmpstr); |
| 254 | } | 109 | } |
| 255 | 110 | ||
| 256 | /* parse and handle server logon */ | 111 | /* parse and handle server logon */ |
| 257 | static void | 112 | static void serverlogin(char *message) { |
| 258 | serverlogin (char *message) | 113 | (void)message; |
| 259 | { | 114 | int utf8 = !strcmp(nl_langinfo(CODESET), "UTF-8"); |
| 260 | int utf8=!strcmp(nl_langinfo(CODESET), "UTF-8"); | ||
| 261 | if (utf8) | 115 | if (utf8) |
| 262 | networkoutput(".e utf8"); | 116 | vc_sendmessage(".e utf8"); |
| 263 | } | 117 | } |
| 264 | 118 | ||
| 265 | /* parse and handle an idle message | 119 | /* parse and handle an idle message |
| 266 | * format: 305 | 120 | * format: 305 |
| 267 | * vars: %s message */ | 121 | * vars: %s message */ |
| 268 | static void | 122 | static void idleprompt(char *message) { |
| 269 | idleprompt (char *message) | ||
| 270 | { | ||
| 271 | char *msg = NULL; | 123 | char *msg = NULL; |
| 272 | msg = strchr (message, ' '); | 124 | msg = strchr(message, ' '); |
| 273 | msg[0] = '\0'; | 125 | msg[0] = '\0'; |
| 274 | msg++; | 126 | msg++; |
| 275 | 127 | ||
| 276 | snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_IDLE),msg); | 128 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_IDLE), msg); |
| 277 | writechan (tmpstr); | 129 | writechan(tmpstr); |
| 278 | } | 130 | } |
| 279 | 131 | ||
| 280 | /* parse and handle a topic information string | 132 | /* parse and handle a topic information string |
| 281 | * format: 115 %d %s | 133 | * format: 115 %d %s |
| 282 | * vars: %d chan - channel number | 134 | * vars: %d chan - channel number |
| 283 | * %s topic - topic */ | 135 | * %s topic - topic */ |
| 284 | static void | 136 | static void topicinfo(char *message) { |
| 285 | topicinfo (char *message) | ||
| 286 | { | ||
| 287 | char *channel = NULL, *topic = NULL; | 137 | char *channel = NULL, *topic = NULL; |
| 288 | int tmpchan = 0, ownchan = own_channel_get(); | 138 | int tmpchan = 0, ownchan = own_channel_get(); |
| 289 | 139 | ||
| 290 | /* search start of channel number */ | 140 | /* search start of channel number */ |
| 291 | channel = strchr (message, ' '); | 141 | channel = strchr(message, ' '); |
| 292 | channel++; | 142 | channel++; |
| 293 | 143 | ||
| 294 | /* search start of topic and terminate channel number */ | 144 | /* search start of topic and terminate channel number */ |
| 295 | topic = strchr (channel, ' '); | 145 | topic = strchr(channel, ' '); |
| 296 | topic[0] = '\0'; | 146 | topic[0] = '\0'; |
| 297 | topic++; | 147 | topic++; |
| 298 | 148 | ||
| 299 | /* convert channel number to integer */ | 149 | /* convert channel number to integer */ |
| 300 | tmpchan = atoi (channel); | 150 | tmpchan = atoi(channel); |
| 301 | 151 | ||
| 302 | if (tmpchan == ownchan ) { | 152 | if (tmpchan == ownchan) { |
| 303 | /* show change in topic window */ | 153 | /* show change in topic window */ |
| 304 | if (strlen(topic)) | 154 | if (strlen(topic)) |
| 305 | snprintf (topicstr, TOPICSTRSIZE, getformatstr(FS_TOPICW), ownchan, topic); | 155 | snprintf(topicstr, TOPICSTRSIZE, getformatstr(FS_TOPICW), ownchan, topic); |
| 306 | else | 156 | else |
| 307 | snprintf (topicstr, TOPICSTRSIZE, getformatstr(FS_NOTOPICW), ownchan ); | 157 | snprintf(topicstr, TOPICSTRSIZE, getformatstr(FS_NOTOPICW), ownchan); |
| 308 | topicline(NULL); | 158 | topicline(NULL); |
| 309 | } | 159 | } |
| 310 | 160 | ||
| 311 | /* announce change in channel window */ | 161 | /* announce change in channel window */ |
| 312 | if (strlen(topic)) | 162 | if (strlen(topic)) |
| 313 | snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_TOPIC), tmpchan, topic); | 163 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_TOPIC), tmpchan, topic); |
| 314 | else | 164 | else |
| 315 | snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_NOTOPIC), tmpchan); | 165 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_NOTOPIC), tmpchan); |
| 316 | writechan (tmpstr); | 166 | writechan(tmpstr); |
| 317 | } | 167 | } |
| 318 | 168 | ||
| 319 | /* parse and handle a topic change string | 169 | /* parse and handle a topic change string |
| 320 | * format: 114 %s changes topic to '%s' | 170 | * format: 114 %s changes topic to '%s' |
| 321 | * vars: %s nick | 171 | * vars: %s nick |
| 322 | * %s topic */ | 172 | * %s topic */ |
| 323 | static void | 173 | static void topicchange(char *message) { |
| 324 | topicchange (char *message) | ||
| 325 | { | ||
| 326 | char *nick = NULL, *topic = NULL; | 174 | char *nick = NULL, *topic = NULL; |
| 327 | int len, ownchan = own_channel_get(); | 175 | int len, ownchan = own_channel_get(); |
| 328 | 176 | ||
| 329 | /* search start of nickname */ | 177 | /* search start of nickname */ |
| 330 | nick = strchr (message, ' '); | 178 | nick = strchr(message, ' '); |
| 331 | nick++; | 179 | nick++; |
| 332 | 180 | ||
| 333 | /* search start of message before topic, terminate nick */ | 181 | /* search start of message before topic, terminate nick */ |
| 334 | topic = strchr (nick, ' '); | 182 | topic = strchr(nick, ' '); |
| 335 | topic[0] = '\0'; | 183 | topic[0] = '\0'; |
| 336 | topic++; | 184 | topic++; |
| 337 | 185 | ||
| 338 | /* search start of real topic and terminate channel number */ | 186 | /* search start of real topic and terminate channel number */ |
| 339 | topic = strchr (topic, '\''); | 187 | topic = strchr(topic, '\''); |
| 340 | topic[0] = '\0'; | 188 | topic[0] = '\0'; |
| 341 | topic++; | 189 | topic++; |
| 342 | 190 | ||
| 343 | /* remove ending '\'', if there */ | 191 | /* remove ending '\'', if there */ |
| 344 | len = strlen (topic); | 192 | len = strlen(topic); |
| 345 | if (topic[len-1] == '\'') | 193 | if (topic[len - 1] == '\'') |
| 346 | topic[len-1] = '\0'; | 194 | topic[len - 1] = '\0'; |
| 347 | 195 | ||
| 348 | ul_public_action(nick); | 196 | ul_public_action(nick); |
| 349 | /* show change in topic window */ | 197 | /* show change in topic window */ |
| 350 | snprintf (topicstr, TOPICSTRSIZE, getformatstr(FS_TOPICW), ownchan, topic); | 198 | snprintf(topicstr, TOPICSTRSIZE, getformatstr(FS_TOPICW), ownchan, topic); |
| 351 | topicline(NULL); | 199 | topicline(NULL); |
| 352 | 200 | ||
| 353 | /* announce change in channel window */ | 201 | /* announce change in channel window */ |
| 354 | snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_CHGTOPIC), nick, topic); | 202 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_CHGTOPIC), nick, topic); |
| 355 | writechan (tmpstr); | 203 | writechan(tmpstr); |
| 356 | } | 204 | } |
| 357 | 205 | ||
| 358 | /* parse and handle a login message | 206 | /* parse and handle a login message |
| 359 | * format: 212 %s %s | 207 | * format: 212 %s %s |
| 360 | * vars: %s str1 - nick used to login | 208 | * vars: %s str1 - nick used to login |
| 361 | * %s str2 - servers message */ | 209 | * %s str2 - servers message */ |
| 362 | static void | 210 | static void justloggedin(char *message) { |
| 363 | justloggedin (char *message) | ||
| 364 | { | ||
| 365 | char *str1 = NULL, *str2 = NULL; | 211 | char *str1 = NULL, *str2 = NULL; |
| 366 | /* search start of nickname */ | 212 | /* search start of nickname */ |
| 367 | str1 = strchr (message, ' '); | 213 | str1 = strchr(message, ' '); |
| 368 | str1++; | 214 | str1++; |
| 369 | 215 | ||
| 370 | /* search start of message, terminate nick */ | 216 | /* search start of message, terminate nick */ |
| 371 | str2 = strchr (str1, ' '); | 217 | str2 = strchr(str1, ' '); |
| 372 | str2[0] = '\0'; | 218 | str2[0] = '\0'; |
| 373 | str2++; | 219 | str2++; |
| 374 | 220 | ||
| 375 | /* if we have a new nick, store it */ | 221 | /* if we have a new nick, store it */ |
| 376 | own_nick_set( str1 ); | 222 | own_nick_set(str1); |
| 377 | 223 | ||
| 378 | /* show change in console window */ | 224 | /* show change in console window */ |
| 379 | snprintf (consolestr, CONSOLESTRSIZE, getformatstr(FS_CONSOLE), str1, getstroption (CF_SERVERHOST), getstroption (CF_SERVERPORT)); | 225 | snprintf(consolestr, CONSOLESTRSIZE, getformatstr(FS_CONSOLE), str1, |
| 380 | consoleline (NULL); | 226 | getstroption(CF_SERVERHOST), getstroption(CF_SERVERPORT)); |
| 227 | consoleline(NULL); | ||
| 381 | 228 | ||
| 382 | /* announce login as servermessage */ | 229 | /* announce login as servermessage */ |
| 383 | snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_SIGNON), str1, str2); | 230 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_SIGNON), str1, str2); |
| 384 | writechan (tmpstr); | 231 | writechan(tmpstr); |
| 385 | 232 | ||
| 386 | /* we're not logged in, change status and request nicks */ | 233 | /* we're not logged in, change status and request nicks */ |
| 387 | if (!loggedin) | 234 | if (!loggedin) { |
| 388 | { | 235 | loadcfg(getstroption(CF_LOGINSCRIPT), 0, handleline); |
| 389 | loadcfg(getstroption(CF_LOGINSCRIPT),0,handleline); | 236 | handleline(".S"); |
| 390 | handleline(".S"); | 237 | loggedin = 1; |
| 391 | loggedin = 1; | 238 | flushconnect(); |
| 392 | } | 239 | } |
| 393 | } | 240 | } |
| 394 | 241 | ||
| 395 | /* this user joins a different channel */ | 242 | /* this user joins a different channel */ |
| 396 | void | 243 | void ownjoin(int channel) { |
| 397 | ownjoin (int channel) | 244 | vc_sendmessage(".t"); |
| 398 | { | 245 | snprintf(tmpstr, TMPSTRSIZE, ".S %d", channel); |
| 399 | networkoutput(".t"); | 246 | vc_sendmessage(tmpstr); |
| 400 | snprintf(tmpstr, TMPSTRSIZE, ".S %d",channel); | ||
| 401 | networkoutput(tmpstr); | ||
| 402 | } | 247 | } |
| 403 | 248 | ||
| 404 | /* this user changes his nick */ | 249 | /* this user changes his nick */ |
| 405 | void | 250 | void ownnickchange(const char *newnick) { (void)newnick; } |
| 406 | ownnickchange (char *newnick) | ||
| 407 | { | ||
| 408 | } | ||
| 409 | 251 | ||
| 410 | /* parse and handle a nick error message | 252 | /* parse and handle a nick error message |
| 411 | * format: 401 %s | 253 | * format: 401 %s |
| 412 | * 403 %s | 254 | * 403 %s |
| 413 | * 415 %s | 255 | * 415 %s |
| 414 | * vars: %s - server message */ | 256 | * vars: %s - server message */ |
| 415 | static void | 257 | static void nickerr(char *message) { |
| 416 | nickerr (char *message) | ||
| 417 | { | ||
| 418 | char *helpkiller = NULL; | 258 | char *helpkiller = NULL; |
| 419 | /* mutate message for output */ | 259 | /* mutate message for output */ |
| 420 | message[2] = '!'; | 260 | message[2] = '!'; |
| 421 | /* help information found? remove it. */ | 261 | /* help information found? remove it. */ |
| 422 | if ((helpkiller = strstr (message, " Type .h for help"))) | 262 | if ((helpkiller = strstr(message, " Type .h for help"))) |
| 423 | helpkiller[0] = '\0'; | 263 | helpkiller[0] = '\0'; |
| 424 | /* nickchange not done? eliminate message */ | 264 | /* nickchange not done? eliminate message */ |
| 425 | if (loggedin && (helpkiller = strstr (message, " - Nick not changed"))) | 265 | if (loggedin && (helpkiller = strstr(message, " - Nick not changed"))) |
| 426 | helpkiller[0] = '\0'; | 266 | helpkiller[0] = '\0'; |
| 427 | /* show errormessage */ | 267 | /* show errormessage */ |
| 428 | writecf (FS_ERR,&message[2]); | 268 | writecf(FS_ERR, &message[2]); |
| 429 | 269 | ||
| 430 | /* not logged in? insist on getting a new nick */ | 270 | /* not logged in? insist on getting a new nick */ |
| 431 | if (!loggedin) | 271 | if (!loggedin) { |
| 432 | { | 272 | /* free bogus nick */ |
| 433 | /* free bogus nick */ | 273 | own_nick_set(NULL); |
| 434 | own_nick_set(NULL); | ||
| 435 | 274 | ||
| 436 | /* get new nick via vchat-ui.c */ | 275 | /* get new nick via vchat-ui.c */ |
| 437 | nickprompt (); | 276 | nickprompt(); |
| 438 | 277 | ||
| 439 | /* form login message and send it to server */ | 278 | /* form login message and send it to server */ |
| 440 | snprintf (tmpstr, TMPSTRSIZE, ".l %s %s %d", own_nick_get(), getstroption (CF_FROM), getintoption (CF_CHANNEL)); | 279 | snprintf(tmpstr, TMPSTRSIZE, ".l %s %s %d", own_nick_get(), |
| 441 | networkoutput (tmpstr); | 280 | getstroption(CF_FROM), getintoption(CF_CHANNEL)); |
| 442 | } | 281 | vc_sendmessage(tmpstr); |
| 282 | } | ||
| 443 | } | 283 | } |
| 444 | 284 | ||
| 445 | /* parse and handle a registered nick information | 285 | /* parse and handle a registered nick information |
| 446 | * format: 120 %s %s | 286 | * format: 120 %s %s |
| 447 | * vars: %s - this users registered nick | 287 | * vars: %s - this users registered nick |
| 448 | * %s msg - server message */ | 288 | * %s msg - server message */ |
| 449 | static void | 289 | static void login(char *message) { |
| 450 | login (char *message) { | ||
| 451 | char *msg = NULL; | 290 | char *msg = NULL; |
| 452 | 291 | ||
| 453 | /* mutate message for output */ | 292 | /* mutate message for output */ |
| 454 | message[2] = '*'; | 293 | message[2] = '*'; |
| 455 | /* show message to user */ | 294 | /* show message to user */ |
| 456 | writecf (FS_SERV,&message[2]); | 295 | writecf(FS_SERV, &message[2]); |
| 457 | 296 | ||
| 458 | /* we don't know our nick? */ | 297 | /* we don't know our nick? */ |
| 459 | if (!own_nick_get() ) { | 298 | if (!own_nick_get()) { |
| 460 | /* find message after nick */ | 299 | /* find message after nick */ |
| 461 | msg = strchr (&message[4], ' '); | 300 | msg = strchr(&message[4], ' '); |
| 462 | if (msg) { | 301 | if (msg) { |
| 463 | /* terminate string before message and copy nick */ | 302 | /* terminate string before message and copy nick */ |
| 464 | msg[0] = '\0'; | 303 | msg[0] = '\0'; |
| 465 | own_nick_set(&message[4]); | 304 | own_nick_set(&message[4]); |
| 466 | } else { | 305 | } else { |
| 467 | /* no string in servers message (huh?), ask user for nick */ | 306 | /* no string in servers message (huh?), ask user for nick */ |
| 468 | nickprompt (); | 307 | nickprompt(); |
| 469 | } | 308 | } |
| 470 | } | 309 | } |
| 471 | 310 | ||
| 472 | /* form login message and send it to server */ | 311 | /* form login message and send it to server */ |
| 473 | snprintf (tmpstr, TMPSTRSIZE, ".l %s %s %d", own_nick_get(), getstroption (CF_FROM), getintoption (CF_CHANNEL)); | 312 | snprintf(tmpstr, TMPSTRSIZE, ".l %s %s %d", own_nick_get(), |
| 474 | networkoutput (tmpstr); | 313 | getstroption(CF_FROM), getintoption(CF_CHANNEL)); |
| 314 | vc_sendmessage(tmpstr); | ||
| 475 | } | 315 | } |
| 476 | 316 | ||
| 477 | /* parse and handle anon login request | 317 | /* parse and handle anon login request |
| 478 | * format: 121 %s | 318 | * format: 121 %s |
| 479 | * vars: %s - server message */ | 319 | * vars: %s - server message */ |
| 480 | static void | 320 | static void anonlogin(char *message) { |
| 481 | anonlogin (char *message) | ||
| 482 | { | ||
| 483 | /* mutate message for output */ | 321 | /* mutate message for output */ |
| 484 | message[2] = '*'; | 322 | message[2] = '*'; |
| 485 | /* show message to user */ | 323 | /* show message to user */ |
| 486 | writecf (FS_SERV,&message[2]); | 324 | writecf(FS_SERV, &message[2]); |
| 487 | 325 | ||
| 488 | /* we don't know our nick? ask for it! */ | 326 | /* we don't know our nick? ask for it! */ |
| 489 | if (!own_nick_get()) | 327 | if (!own_nick_get()) |
| 490 | nickprompt (); | 328 | nickprompt(); |
| 491 | 329 | ||
| 492 | /* form login message and send it to server */ | 330 | /* form login message and send it to server */ |
| 493 | snprintf (tmpstr, TMPSTRSIZE, ".l %s %s %d", own_nick_get(), getstroption (CF_FROM), getintoption (CF_CHANNEL)); | 331 | snprintf(tmpstr, TMPSTRSIZE, ".l %s %s %d", own_nick_get(), |
| 494 | networkoutput (tmpstr); | 332 | getstroption(CF_FROM), getintoption(CF_CHANNEL)); |
| 333 | vc_sendmessage(tmpstr); | ||
| 495 | } | 334 | } |
| 496 | 335 | ||
| 497 | /* parse and handle list of nicks (from '.S') | 336 | /* parse and handle list of nicks (from '.S') |
| 498 | * format: 119 %s .. | 337 | * format: 119 %s .. |
| 499 | * vars: %s nick - a users nick */ | 338 | * vars: %s nick - a users nick */ |
| 500 | static void | 339 | static void receivenicks(char *message) { |
| 501 | receivenicks (char *message) { | ||
| 502 | char *str1 = NULL, *str2 = NULL; | 340 | char *str1 = NULL, *str2 = NULL; |
| 503 | int chanflag = -1; | 341 | int chanflag = -1; |
| 504 | 342 | ||
| 505 | /* show message to user */ | 343 | /* show message to user */ |
| 506 | snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_USONLINE), &message[4]); | 344 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_USONLINE), &message[4]); |
| 507 | writechan (tmpstr); | 345 | writechan(tmpstr); |
| 508 | 346 | ||
| 509 | /* search for channelnumber */ | 347 | /* search for channelnumber */ |
| 510 | if( !(str1 = strchr (message, ' ') ) ) return; | 348 | if (!(str1 = strchr(message, ' '))) |
| 349 | return; | ||
| 511 | str1++; | 350 | str1++; |
| 512 | 351 | ||
| 513 | if (str1[0] == '*') { | 352 | if (str1[0] == '*') { |
| 514 | ul_rebuild_list(); | 353 | ul_rebuild_list(); |
| 515 | str1++; | 354 | str1++; |
| 516 | } else { | 355 | } else { |
| 517 | int mychan; | 356 | int mychan; |
| 518 | str2 = str1; | 357 | str2 = str1; |
| 519 | str1 = strchr(str2,' '); | 358 | str1 = strchr(str2, ' '); |
| 520 | str1[0] = '\0'; | 359 | str1[0] = '\0'; |
| 521 | mychan = atoi(str2); | 360 | mychan = atoi(str2); |
| 522 | if( mychan != own_channel_get() ) | 361 | if (mychan != own_channel_get()) |
| 523 | return; | 362 | return; |
| 524 | 363 | ||
| 525 | /* Kick all users from the IN_MY_CHANNEL list */ | 364 | /* Kick all users from the IN_MY_CHANNEL list */ |
| 526 | own_channel_set( own_channel_get() ); | 365 | own_channel_set(own_channel_get()); |
| 527 | chanflag = 1; | 366 | chanflag = 1; |
| 528 | } | 367 | } |
| 529 | str1++; | 368 | str1++; |
| 530 | 369 | ||
| 531 | /* while user .. */ | 370 | /* while user .. */ |
| 532 | while (str1) { | 371 | while (str1) { |
| 533 | /* search next user */ | 372 | /* search next user */ |
| 534 | str2 = strchr (str1, ' '); | 373 | str2 = strchr(str1, ' '); |
| 535 | /* there is another user? terminate this one */ | 374 | /* there is another user? terminate this one */ |
| 536 | if (str2) { | 375 | if (str2) { |
| 537 | str2[0] = '\0'; | 376 | str2[0] = '\0'; |
| @@ -551,52 +390,48 @@ receivenicks (char *message) { | |||
| 551 | * format: 211 %s %s | 390 | * format: 211 %s %s |
| 552 | * vars: %s nick - who logged on | 391 | * vars: %s nick - who logged on |
| 553 | * %s msg - servers message */ | 392 | * %s msg - servers message */ |
| 554 | static void | 393 | static void usersignon(char *message) { |
| 555 | usersignon (char *message) | ||
| 556 | { | ||
| 557 | char *nick = NULL, *msg = NULL; | 394 | char *nick = NULL, *msg = NULL; |
| 558 | /* search start of nickname */ | 395 | /* search start of nickname */ |
| 559 | nick = strchr (message, ' '); | 396 | nick = strchr(message, ' '); |
| 560 | nick++; | 397 | nick++; |
| 561 | 398 | ||
| 562 | /* search start of message, terminate nick */ | 399 | /* search start of message, terminate nick */ |
| 563 | msg = strchr (nick, ' '); | 400 | msg = strchr(nick, ' '); |
| 564 | msg[0] = '\0'; | 401 | msg[0] = '\0'; |
| 565 | msg++; | 402 | msg++; |
| 566 | 403 | ||
| 567 | /* add this user via vchat-user.c */ | 404 | /* add this user via vchat-user.c */ |
| 568 | ul_add (nick, 0); | 405 | ul_add(nick, 0); |
| 569 | 406 | ||
| 570 | /* show message to user */ | 407 | /* show message to user */ |
| 571 | snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_SIGNON), nick, msg); | 408 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_SIGNON), nick, msg); |
| 572 | writechan (tmpstr); | 409 | writechan(tmpstr); |
| 573 | } | 410 | } |
| 574 | 411 | ||
| 575 | /* parse and handle a logoff message | 412 | /* parse and handle a logoff message |
| 576 | * format: 221 %s %s | 413 | * format: 221 %s %s |
| 577 | * vars: %s nick - who logged off | 414 | * vars: %s nick - who logged off |
| 578 | * %s msg - servers message */ | 415 | * %s msg - servers message */ |
| 579 | static void | 416 | static void usersignoff(char *message) { |
| 580 | usersignoff (char *message) | ||
| 581 | { | ||
| 582 | char *nick = NULL, *msg = NULL; | 417 | char *nick = NULL, *msg = NULL; |
| 583 | /* search start of nickname */ | 418 | /* search start of nickname */ |
| 584 | nick = strchr (message, ' '); | 419 | nick = strchr(message, ' '); |
| 585 | nick++; | 420 | nick++; |
| 586 | 421 | ||
| 587 | /* search start of message, terminate nick */ | 422 | /* search start of message, terminate nick */ |
| 588 | msg = strchr (nick, ' '); | 423 | msg = strchr(nick, ' '); |
| 589 | if( msg ) { | 424 | if (msg) { |
| 590 | msg[0] = '\0'; | 425 | msg[0] = '\0'; |
| 591 | msg++; | 426 | msg++; |
| 592 | } | 427 | } |
| 593 | 428 | ||
| 594 | /* delete this user via vchat-user.c */ | 429 | /* delete this user via vchat-user.c */ |
| 595 | ul_del (nick); | 430 | ul_del(nick); |
| 596 | 431 | ||
| 597 | /* show message to user */ | 432 | /* show message to user */ |
| 598 | snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_SIGNOFF), nick, msg); | 433 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_SIGNOFF), nick, msg); |
| 599 | writechan (tmpstr); | 434 | writechan(tmpstr); |
| 600 | } | 435 | } |
| 601 | 436 | ||
| 602 | /* parse and handle a join message | 437 | /* parse and handle a join message |
| @@ -604,40 +439,38 @@ usersignoff (char *message) | |||
| 604 | * vars: %s nick - who joined | 439 | * vars: %s nick - who joined |
| 605 | * %s msg - servers message | 440 | * %s msg - servers message |
| 606 | * %d chan - channel joined */ | 441 | * %d chan - channel joined */ |
| 607 | static void | 442 | static void userjoin(char *message) { |
| 608 | userjoin (char *message) | ||
| 609 | { | ||
| 610 | char *nick = NULL, *msg = NULL, *channel = NULL; | 443 | char *nick = NULL, *msg = NULL, *channel = NULL; |
| 611 | int chan = 0; | 444 | int chan = 0; |
| 612 | 445 | ||
| 613 | /* search start of nickname */ | 446 | /* search start of nickname */ |
| 614 | nick = strchr (message, ' '); | 447 | nick = strchr(message, ' '); |
| 615 | nick++; | 448 | nick++; |
| 616 | 449 | ||
| 617 | /* search start of message, terminate nick */ | 450 | /* search start of message, terminate nick */ |
| 618 | msg = strchr (nick, ' '); | 451 | msg = strchr(nick, ' '); |
| 619 | msg[0] = '\0'; | 452 | msg[0] = '\0'; |
| 620 | msg++; | 453 | msg++; |
| 621 | 454 | ||
| 622 | /* search start of channel, terminate message */ | 455 | /* search start of channel, terminate message */ |
| 623 | channel = strrchr (msg, ' '); | 456 | channel = strrchr(msg, ' '); |
| 624 | channel[0] = '\0'; | 457 | channel[0] = '\0'; |
| 625 | channel++; | 458 | channel++; |
| 626 | 459 | ||
| 627 | /* convert channel to integer */ | 460 | /* convert channel to integer */ |
| 628 | chan = atoi (channel); | 461 | chan = atoi(channel); |
| 629 | 462 | ||
| 630 | /* is it myself joining */ | 463 | /* is it myself joining */ |
| 631 | if( own_nick_check(nick) ) | 464 | if (own_nick_check(nick)) |
| 632 | own_channel_set(chan); | 465 | own_channel_set(chan); |
| 633 | 466 | ||
| 634 | /* notice channel join via vchat-user.c */ | 467 | /* notice channel join via vchat-user.c */ |
| 635 | if( own_channel_get() == chan ) | 468 | if (own_channel_get() == chan) |
| 636 | ul_enter_chan(nick); | 469 | ul_enter_chan(nick); |
| 637 | 470 | ||
| 638 | /* show message to user */ | 471 | /* show message to user */ |
| 639 | snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_JOIN), nick, msg, chan); | 472 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_JOIN), nick, msg, chan); |
| 640 | writechan (tmpstr); | 473 | writechan(tmpstr); |
| 641 | } | 474 | } |
| 642 | 475 | ||
| 643 | /* parse and handle a leave message | 476 | /* parse and handle a leave message |
| @@ -645,35 +478,33 @@ userjoin (char *message) | |||
| 645 | * vars: %s nick - who left | 478 | * vars: %s nick - who left |
| 646 | * %s msg - servers message | 479 | * %s msg - servers message |
| 647 | * %d chan - channel joined */ | 480 | * %d chan - channel joined */ |
| 648 | static void | 481 | static void userleave(char *message) { |
| 649 | userleave (char *message) | ||
| 650 | { | ||
| 651 | char *nick = NULL, *msg = NULL, *channel = NULL; | 482 | char *nick = NULL, *msg = NULL, *channel = NULL; |
| 652 | int chan = 0; | 483 | int chan = 0; |
| 653 | 484 | ||
| 654 | /* search start of nickname */ | 485 | /* search start of nickname */ |
| 655 | nick = strchr (message, ' '); | 486 | nick = strchr(message, ' '); |
| 656 | nick++; | 487 | nick++; |
| 657 | 488 | ||
| 658 | /* search start of message, terminate nick */ | 489 | /* search start of message, terminate nick */ |
| 659 | msg = strchr (nick, ' '); | 490 | msg = strchr(nick, ' '); |
| 660 | msg[0] = '\0'; | 491 | msg[0] = '\0'; |
| 661 | msg++; | 492 | msg++; |
| 662 | 493 | ||
| 663 | /* convert channel to integer */ | 494 | /* convert channel to integer */ |
| 664 | channel = strrchr (msg, ' '); | 495 | channel = strrchr(msg, ' '); |
| 665 | channel[0] = '\0'; | 496 | channel[0] = '\0'; |
| 666 | channel++; | 497 | channel++; |
| 667 | 498 | ||
| 668 | /* convert channel to integer */ | 499 | /* convert channel to integer */ |
| 669 | chan = atoi (channel); | 500 | chan = atoi(channel); |
| 670 | 501 | ||
| 671 | /* notice channel leave via vchat-user.c */ | 502 | /* notice channel leave via vchat-user.c */ |
| 672 | ul_leave_chan(nick); | 503 | ul_leave_chan(nick); |
| 673 | 504 | ||
| 674 | /* show message to user */ | 505 | /* show message to user */ |
| 675 | snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_LEAVE), nick, msg, chan); | 506 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_LEAVE), nick, msg, chan); |
| 676 | writechan (tmpstr); | 507 | writechan(tmpstr); |
| 677 | } | 508 | } |
| 678 | 509 | ||
| 679 | /* parse and handle a nickchange message | 510 | /* parse and handle a nickchange message |
| @@ -681,242 +512,140 @@ userleave (char *message) | |||
| 681 | * vars: %s oldnick - users old nick | 512 | * vars: %s oldnick - users old nick |
| 682 | * %s newnick - users new nick | 513 | * %s newnick - users new nick |
| 683 | * %s msg - server message */ | 514 | * %s msg - server message */ |
| 684 | static void | 515 | static void usernickchange(char *message) { |
| 685 | usernickchange (char *message) | ||
| 686 | { | ||
| 687 | char *oldnick = NULL, *newnick = NULL, *msg = NULL; | 516 | char *oldnick = NULL, *newnick = NULL, *msg = NULL; |
| 688 | 517 | ||
| 689 | /* search start of old nickname */ | 518 | /* search start of old nickname */ |
| 690 | oldnick = strchr (message, ' '); | 519 | oldnick = strchr(message, ' '); |
| 691 | oldnick++; | 520 | oldnick++; |
| 692 | 521 | ||
| 693 | /* search start of new nickname, terminate old nick */ | 522 | /* search start of new nickname, terminate old nick */ |
| 694 | newnick = strchr (oldnick, ' '); | 523 | newnick = strchr(oldnick, ' '); |
| 695 | newnick[0] = '\0'; | 524 | newnick[0] = '\0'; |
| 696 | newnick++; | 525 | newnick++; |
| 697 | 526 | ||
| 698 | /* search start of message, terminate new nick */ | 527 | /* search start of message, terminate new nick */ |
| 699 | msg = strchr (newnick, ' '); | 528 | msg = strchr(newnick, ' '); |
| 700 | msg[0] = '\0'; | 529 | msg[0] = '\0'; |
| 701 | msg++; | 530 | msg++; |
| 702 | 531 | ||
| 703 | /* notice nickchange via vchat-user.c */ | 532 | /* notice nickchange via vchat-user.c */ |
| 704 | ul_rename (oldnick, newnick); | 533 | ul_rename(oldnick, newnick); |
| 705 | 534 | ||
| 706 | if( own_nick_check(newnick) ) { | 535 | if (own_nick_check(newnick)) { |
| 707 | /* show change in console window */ | 536 | /* show change in console window */ |
| 708 | snprintf (consolestr, CONSOLESTRSIZE, getformatstr(FS_CONSOLE), newnick, getstroption (CF_SERVERHOST), getstroption (CF_SERVERPORT)); | 537 | snprintf(consolestr, CONSOLESTRSIZE, getformatstr(FS_CONSOLE), newnick, |
| 709 | consoleline (NULL); | 538 | getstroption(CF_SERVERHOST), getstroption(CF_SERVERPORT)); |
| 539 | consoleline(NULL); | ||
| 710 | } | 540 | } |
| 711 | 541 | ||
| 712 | /* show message to user */ | 542 | /* show message to user */ |
| 713 | snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_NICKCHANGE), oldnick, newnick, msg); | 543 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_NICKCHANGE), oldnick, newnick, |
| 714 | writechan (tmpstr); | 544 | msg); |
| 545 | writechan(tmpstr); | ||
| 715 | } | 546 | } |
| 716 | 547 | ||
| 717 | /* handle received message from server */ | 548 | /* handle received message from server */ |
| 718 | static void | 549 | void protocol_parsemsg(char *message) { |
| 719 | parsemsg (char *message) | ||
| 720 | { | ||
| 721 | char *str1, *str2; | 550 | char *str1, *str2; |
| 722 | int i; | 551 | int i; |
| 723 | /* message to short or starts with '<'? must be channel */ | 552 | /* message to short or starts with '<'? must be channel */ |
| 724 | if (message[0] == '<') | 553 | if (message[0] == '<') { |
| 725 | { | ||
| 726 | str1 = &message[1]; | 554 | str1 = &message[1]; |
| 727 | str2 = strchr(str1,'>'); | 555 | str2 = strchr(str1, '>'); |
| 728 | str2[0] = '\0'; | 556 | str2[0] = '\0'; |
| 729 | str2++; | 557 | str2++; |
| 730 | if (str2[0] == ' ') str2++; | 558 | if (str2[0] == ' ') |
| 559 | str2++; | ||
| 731 | if (own_nick_check(str1)) | 560 | if (own_nick_check(str1)) |
| 732 | snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_MYPUBMSG),str1,str2); | 561 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_MYPUBMSG), str1, str2); |
| 733 | else | 562 | else |
| 734 | snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_RXPUBMSG),str1,str2); | 563 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_RXPUBMSG), str1, str2); |
| 735 | writechan (tmpstr); | 564 | writechan(tmpstr); |
| 736 | 565 | ||
| 737 | ul_public_action(str1); | 566 | ul_public_action(str1); |
| 738 | } | 567 | } else if (message[0] == '[') { |
| 739 | else if (message[0] == '[') | ||
| 740 | { | ||
| 741 | str1 = &message[1]; | 568 | str1 = &message[1]; |
| 742 | str2 = strchr(str1,']'); | 569 | str2 = strchr(str1, ']'); |
| 743 | str2[0] = '\0'; | 570 | str2[0] = '\0'; |
| 744 | str2++; | 571 | str2++; |
| 745 | if (str2[0] == ' ') str2++; | 572 | if (str2[0] == ' ') |
| 746 | if (own_nick_check( str1 )) | 573 | str2++; |
| 747 | snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_MYPUBURL),str1,str2); | 574 | if (own_nick_check(str1)) |
| 575 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_MYPUBURL), str1, str2); | ||
| 748 | else | 576 | else |
| 749 | snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_RXPUBURL),str1,str2); | 577 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_RXPUBURL), str1, str2); |
| 750 | ul_public_action(str1); | 578 | ul_public_action(str1); |
| 751 | writechan (tmpstr); | 579 | writechan(tmpstr); |
| 752 | } | 580 | } |
| 753 | /* message starts with '*'? must be private */ | 581 | /* message starts with '*'? must be private */ |
| 754 | else if (message[0] == '*') | 582 | else if (message[0] == '*') { |
| 755 | { | ||
| 756 | str1 = &message[1]; | 583 | str1 = &message[1]; |
| 757 | str2 = strchr(str1,'*'); | 584 | str2 = strchr(str1, '*'); |
| 758 | str2[0] = '\0'; | 585 | str2[0] = '\0'; |
| 759 | str2++; | 586 | str2++; |
| 760 | if (str2[0] == ' ') str2++; | 587 | if (str2[0] == ' ') |
| 761 | snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_RXPRIVMSG),str1,str2); | 588 | str2++; |
| 762 | writepriv (tmpstr, 1); | 589 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_RXPRIVMSG), str1, str2); |
| 590 | writepriv(tmpstr, 1); | ||
| 763 | ul_private_action(str1); | 591 | ul_private_action(str1); |
| 764 | } | 592 | } |
| 765 | /* message starts with a number? must be a servermessage */ | 593 | /* message starts with a number? must be a servermessage */ |
| 766 | else if ((message[0] >= '0') && (message[0] <= '9')) | 594 | else if ((message[0] >= '0') && (message[0] <= '9')) { |
| 767 | { | 595 | /* walk server message array */ |
| 768 | /* walk server message array */ | 596 | for (i = 0; servermessages[i].id[0]; i++) { |
| 769 | for (i = 0; servermessages[i].id[0]; i++) | 597 | /* is this the message? */ |
| 770 | { | 598 | if (!(strncmp(servermessages[i].id, message, 3))) { |
| 771 | /* is this the message? */ | 599 | /* scripting hook available - call it */ |
| 772 | if (!(strncmp (servermessages[i].id, message, 3))) | 600 | if (servermessages[i].hook) |
| 773 | { | 601 | servermessages[i].hook(message); |
| 774 | /* scripting hook available - call it */ | 602 | /* function available - call it */ |
| 775 | if (servermessages[i].hook) | 603 | else if (servermessages[i].funct) |
| 776 | servermessages[i].hook (message); | 604 | servermessages[i].funct(message); |
| 777 | /* function available - call it */ | 605 | /* no function available, but known - give output */ |
| 778 | else if (servermessages[i].funct) | 606 | else { |
| 779 | servermessages[i].funct (message); | 607 | /* remove continutation mark */ |
| 780 | /* no function available, but known - give output */ | 608 | if (message[3] == '-') |
| 781 | else | 609 | message[3] = ' '; |
| 782 | { | 610 | |
| 783 | /* remove continutation mark */ | 611 | /* mutate message for display */ |
| 784 | if (message[3] == '-') | 612 | message[2] = '*'; |
| 785 | message[3] = ' '; | 613 | /* giveout message by type */ |
| 786 | 614 | switch (servermessages[i].type) { | |
| 787 | /* mutate message for display */ | 615 | case SM_IGNORE: |
| 788 | message[2] = '*'; | 616 | break; |
| 789 | /* giveout message by type */ | 617 | case SM_INFO: |
| 790 | switch (servermessages[i].type) | 618 | /* change marker and send as servermessage */ |
| 791 | { | 619 | message[2] = '#'; |
| 792 | case SM_IGNORE: | 620 | writecf(FS_SERV, &message[2]); |
| 793 | break; | 621 | break; |
| 794 | case SM_INFO: | 622 | case SM_USERINFO: |
| 795 | /* change marker and send as servermessage */ | 623 | /* keep marker and send as servermessage */ |
| 796 | message[2] = '#'; | 624 | writecf(FS_SERV, &message[2]); |
| 797 | writecf (FS_SERV,&message[2]); | 625 | break; |
| 798 | break; | 626 | case SM_CHANNEL: |
| 799 | case SM_USERINFO: | 627 | /* keep marker and send as channelmessage */ |
| 800 | /* keep marker and send as servermessage */ | 628 | writechan(&message[2]); |
| 801 | writecf (FS_SERV,&message[2]); | 629 | break; |
| 802 | break; | 630 | case SM_ERROR: |
| 803 | case SM_CHANNEL: | 631 | /* change marker and send as errormessage */ |
| 804 | /* keep marker and send as channelmessage */ | 632 | message[2] = '!'; |
| 805 | writechan (&message[2]); | 633 | writecf(FS_ERR, &message[2]); |
| 806 | break; | 634 | break; |
| 807 | case SM_ERROR: | 635 | default: |
| 808 | /* change marker and send as errormessage */ | 636 | /* fallback: keep marker and send as servermessage */ |
| 809 | message[2] = '!'; | 637 | writecf(FS_SERV, &message[2]); |
| 810 | writecf (FS_ERR,&message[2]); | 638 | } |
| 811 | break; | 639 | } |
| 812 | default: | 640 | return; |
| 813 | /* fallback: keep marker and send as servermessage */ | ||
| 814 | writecf (FS_SERV,&message[2]); | ||
| 815 | } | ||
| 816 | } | ||
| 817 | return; | ||
| 818 | } | ||
| 819 | } | ||
| 820 | /* message not in list, raise errormessage */ | ||
| 821 | snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_UNKNOWNMSG), message); | ||
| 822 | writecf (FS_ERR,tmpstr); | ||
| 823 | } | ||
| 824 | else | ||
| 825 | { | ||
| 826 | /* message neither public, private or server, raise errormessage */ | ||
| 827 | snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_BOGUSMSG), message); | ||
| 828 | writecf (FS_ERR,tmpstr); | ||
| 829 | } | ||
| 830 | } | ||
| 831 | |||
| 832 | /* offset in buffer (for linebreaks at packet borders) */ | ||
| 833 | static int bufoff = 0; | ||
| 834 | |||
| 835 | /* get data from servers filedescriptor */ | ||
| 836 | void | ||
| 837 | networkinput (void) | ||
| 838 | { | ||
| 839 | int bytes; | ||
| 840 | char *tmp = NULL; | ||
| 841 | #define BUFSIZE 4096 | ||
| 842 | char buf[BUFSIZE]; /* data buffer */ | ||
| 843 | char *ltmp = buf; | ||
| 844 | buf[BUFSIZE-1] = '\0'; /* sanity stop */ | ||
| 845 | |||
| 846 | /* receive data at offset */ | ||
| 847 | bytes = BIO_read (server_conn, &buf[bufoff], BUFSIZE-1 - bufoff); | ||
| 848 | |||
| 849 | /* no bytes transferred? raise error message, bail out */ | ||
| 850 | if (bytes < 0) | ||
| 851 | { | ||
| 852 | snprintf (tmpstr, TMPSTRSIZE, "Receive fails, %s.", strerror(errno)); | ||
| 853 | strncpy(errstr,tmpstr,TMPSTRSIZE-2); | ||
| 854 | errstr[TMPSTRSIZE-2] = '\0'; | ||
| 855 | strcat(errstr,"\n"); | ||
| 856 | writecf (FS_ERR,tmpstr); | ||
| 857 | status = 0; | ||
| 858 | } | ||
| 859 | /* end of file from server? */ | ||
| 860 | else if (bytes == 0) | ||
| 861 | { | ||
| 862 | /* inform user, bail out */ | ||
| 863 | writecf (FS_SERV,"* EOF from server"); | ||
| 864 | strncpy(errstr,"* EOF from server",TMPSTRSIZE-2); | ||
| 865 | errstr[TMPSTRSIZE-2] = '\0'; | ||
| 866 | strcat(errstr,"\n"); | ||
| 867 | status = 0; | ||
| 868 | } | ||
| 869 | else | ||
| 870 | { | ||
| 871 | /* terminate message */ | ||
| 872 | buf[bytes + bufoff] = '\0'; | ||
| 873 | /* as long as there are lines .. */ | ||
| 874 | while ((tmp = strchr (ltmp, '\n')) != NULL) | ||
| 875 | { | ||
| 876 | /* did the server send CR+LF instead of LF with the last line? */ | ||
| 877 | if (tmp[-1] == '\r') | ||
| 878 | tmp[-1] = '\0'; | ||
| 879 | |||
| 880 | /* remove newline from previous message, advance pointer of next | ||
| 881 | * message */ | ||
| 882 | tmp[0] = '\0'; | ||
| 883 | tmp++; | ||
| 884 | |||
| 885 | /* we have a last message? give away to line handler! */ | ||
| 886 | if (ltmp[0]) | ||
| 887 | { | ||
| 888 | #ifdef DEBUG | ||
| 889 | /* debugging? log network input! */ | ||
| 890 | fprintf (stderr, "<| %s\n", ltmp); | ||
| 891 | #endif | ||
| 892 | parsemsg (ltmp); | ||
| 893 | } | ||
| 894 | |||
| 895 | /* move line along .. */ | ||
| 896 | ltmp = tmp; | ||
| 897 | } | 641 | } |
| 898 | /* buffer exhausted, move partial line to start of buffer and go on .. */ | ||
| 899 | bufoff = (bytes+bufoff) - (ltmp-buf); | ||
| 900 | if (bufoff > 0) | ||
| 901 | memmove (buf, ltmp, bufoff); | ||
| 902 | else | ||
| 903 | bufoff = 0; | ||
| 904 | } | 642 | } |
| 905 | } | 643 | /* message not in list, raise errormessage */ |
| 906 | 644 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_UNKNOWNMSG), message); | |
| 907 | void | 645 | writecf(FS_ERR, tmpstr); |
| 908 | networkoutput (char *msg) | 646 | } else { |
| 909 | { | 647 | /* message neither public, private or server, raise errormessage */ |
| 910 | #ifdef DEBUG | 648 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_BOGUSMSG), message); |
| 911 | /* debugging? log network output! */ | 649 | writecf(FS_ERR, tmpstr); |
| 912 | fprintf (stderr, ">| %s\n", msg); | 650 | } |
| 913 | #endif | ||
| 914 | |||
| 915 | /* send data to server */ | ||
| 916 | if (BIO_write (server_conn, msg, strlen (msg)) != strlen (msg)) | ||
| 917 | writecf (FS_ERR,"Message sending fuzzy."); | ||
| 918 | |||
| 919 | /* send line termination to server */ | ||
| 920 | if (BIO_write (server_conn, "\r\n", 2) != 2) | ||
| 921 | writecf (FS_ERR,"Message sending fuzzy."); | ||
| 922 | } | 651 | } |
diff --git a/vchat-ssl.c b/vchat-ssl.c deleted file mode 100755 index adef70d..0000000 --- a/vchat-ssl.c +++ /dev/null | |||
| @@ -1,475 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * vchat-client - alpha version | ||
| 3 | * vchat-ssl.c - handling of SSL connection and X509 certificate | ||
| 4 | * verification | ||
| 5 | * | ||
| 6 | * Copyright (C) 2007 Thorsten Schroeder <ths@berlin.ccc.de> | ||
| 7 | * | ||
| 8 | * This program is free software. It can be redistributed and/or modified, | ||
| 9 | * provided that this copyright notice is kept intact. This program is | ||
| 10 | * distributed in the hope that it will be useful, but without any warranty; | ||
| 11 | * without even the implied warranty of merchantability or fitness for a | ||
| 12 | * particular purpose. In no event shall the copyright holder be liable for | ||
| 13 | * any direct, indirect, incidental or special damages arising in any way out | ||
| 14 | * of the use of this software. | ||
| 15 | * | ||
| 16 | */ | ||
| 17 | |||
| 18 | #include <stdio.h> | ||
| 19 | #include <stdlib.h> | ||
| 20 | #include <string.h> | ||
| 21 | #include <assert.h> | ||
| 22 | |||
| 23 | #include <openssl/err.h> | ||
| 24 | #include <openssl/ssl.h> | ||
| 25 | #include <openssl/bio.h> | ||
| 26 | #include <openssl/evp.h> | ||
| 27 | #include <openssl/x509.h> | ||
| 28 | #include <openssl/x509v3.h> | ||
| 29 | #include <openssl/conf.h> | ||
| 30 | |||
| 31 | #include <readline/readline.h> | ||
| 32 | |||
| 33 | #include "vchat.h" | ||
| 34 | #include "vchat-ssl.h" | ||
| 35 | |||
| 36 | const char *vchat_ssl_version = "vchat-ssl.c $Id$"; | ||
| 37 | |||
| 38 | typedef int (*vc_x509verify_cb_t)(int, X509_STORE_CTX *); | ||
| 39 | struct vc_x509store_t { | ||
| 40 | char *cafile; | ||
| 41 | char *capath; | ||
| 42 | char *crlfile; | ||
| 43 | vc_x509verify_cb_t callback; | ||
| 44 | vc_askpass_cb_t askpass_callback; | ||
| 45 | STACK_OF(X509) *certs; | ||
| 46 | STACK_OF(X509_CRL) *crls; | ||
| 47 | char *use_certfile; | ||
| 48 | STACK_OF(X509) *use_certs; | ||
| 49 | char *use_keyfile; | ||
| 50 | EVP_PKEY *use_key; | ||
| 51 | int flags; | ||
| 52 | }; | ||
| 53 | |||
| 54 | static void vc_cleanup_x509store(vc_x509store_t *); // Should not be static but is unused | ||
| 55 | static SSL_CTX * vc_create_sslctx( vc_x509store_t *vc_store ); | ||
| 56 | static int vc_verify_callback(int, X509_STORE_CTX *); | ||
| 57 | static X509_STORE * vc_x509store_create(vc_x509store_t *); | ||
| 58 | static void vc_x509store_clearflags(vc_x509store_t *, int); | ||
| 59 | static void vc_x509store_setcafile(vc_x509store_t *, char *); | ||
| 60 | static void vc_x509store_setcapath(vc_x509store_t *, char *); | ||
| 61 | static void vc_x509store_setcrlfile(vc_x509store_t *, char *); | ||
| 62 | static void vc_x509store_addcert(vc_x509store_t *, X509 *); | ||
| 63 | static void vc_x509store_setcb(vc_x509store_t *, vc_x509verify_cb_t); | ||
| 64 | |||
| 65 | #define VC_CTX_ERR_EXIT(se, cx) do { \ | ||
| 66 | snprintf(tmpstr, TMPSTRSIZE, "CREATE CTX: %s", \ | ||
| 67 | ERR_error_string (ERR_get_error (), NULL)); \ | ||
| 68 | writecf(FS_ERR, tmpstr); \ | ||
| 69 | if(se) X509_STORE_free(se); \ | ||
| 70 | if(cx) SSL_CTX_free(cx); \ | ||
| 71 | return(0); \ | ||
| 72 | } while(0) | ||
| 73 | |||
| 74 | #define VC_SETCERT_ERR_EXIT(se, cx, err) do { \ | ||
| 75 | snprintf(tmpstr, TMPSTRSIZE, "CREATE CTX: %s", err); \ | ||
| 76 | writecf(FS_ERR, tmpstr); \ | ||
| 77 | if(se) X509_STORE_free(se); \ | ||
| 78 | if(cx) SSL_CTX_free(cx); \ | ||
| 79 | return(NULL); \ | ||
| 80 | } while(0) | ||
| 81 | |||
| 82 | static SSL_CTX * vc_create_sslctx( vc_x509store_t *vc_store ) | ||
| 83 | { | ||
| 84 | int i = 0; | ||
| 85 | int n = 0; | ||
| 86 | int flags = 0; | ||
| 87 | int r = 0; | ||
| 88 | SSL_CTX *ctx = NULL; | ||
| 89 | X509_STORE *store = NULL; | ||
| 90 | vc_x509verify_cb_t verify_callback = NULL; | ||
| 91 | |||
| 92 | /* Explicitly use TLSv1 (or maybe later) */ | ||
| 93 | if( !(ctx = SSL_CTX_new(SSLv23_client_method())) ) | ||
| 94 | VC_CTX_ERR_EXIT(store, ctx); | ||
| 95 | |||
| 96 | if( !(store = vc_x509store_create(vc_store)) ) | ||
| 97 | VC_CTX_ERR_EXIT(store, ctx); | ||
| 98 | |||
| 99 | SSL_CTX_set_cert_store(ctx, store); | ||
| 100 | store = NULL; | ||
| 101 | /* Disable some insecure protocols explicitly */ | ||
| 102 | SSL_CTX_set_options(ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); | ||
| 103 | if (getstroption(CF_CIPHERSUITE)) | ||
| 104 | SSL_CTX_set_cipher_list(ctx, getstroption(CF_CIPHERSUITE)); | ||
| 105 | else | ||
| 106 | SSL_CTX_set_cipher_list(ctx, "ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA"); | ||
| 107 | |||
| 108 | SSL_CTX_set_verify_depth (ctx, getintoption(CF_VERIFYSSL)); | ||
| 109 | |||
| 110 | if( !(verify_callback = vc_store->callback) ) | ||
| 111 | verify_callback = vc_verify_callback; | ||
| 112 | |||
| 113 | if( !(vc_store->flags & VC_X509S_SSL_VERIFY_MASK) ) { | ||
| 114 | writecf(FS_DBG, tmpstr); | ||
| 115 | flags = SSL_VERIFY_NONE; | ||
| 116 | } | ||
| 117 | else { | ||
| 118 | if(vc_store->flags & VC_X509S_SSL_VERIFY_PEER) | ||
| 119 | flags |= SSL_VERIFY_PEER; | ||
| 120 | if(vc_store->flags & VC_X509S_SSL_VERIFY_FAIL_IF_NO_PEER_CERT) | ||
| 121 | flags |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; | ||
| 122 | if(vc_store->flags & VC_X509S_SSL_VERIFY_CLIENT_ONCE) | ||
| 123 | flags |= SSL_VERIFY_CLIENT_ONCE; | ||
| 124 | } | ||
| 125 | |||
| 126 | SSL_CTX_set_verify(ctx, flags, verify_callback); | ||
| 127 | |||
| 128 | if(vc_store->flags & VC_X509S_USE_CERTIFICATE) { | ||
| 129 | if(vc_store->use_certfile) | ||
| 130 | SSL_CTX_use_certificate_chain_file(ctx, vc_store->use_certfile); | ||
| 131 | else { | ||
| 132 | SSL_CTX_use_certificate(ctx, | ||
| 133 | sk_X509_value(vc_store->use_certs, 0)); | ||
| 134 | for(i=0,n=sk_X509_num(vc_store->use_certs); i<n; i++) | ||
| 135 | SSL_CTX_add_extra_chain_cert(ctx, | ||
| 136 | sk_X509_value(vc_store->use_certs, i)); | ||
| 137 | } | ||
| 138 | |||
| 139 | SSL_CTX_set_default_passwd_cb(ctx, vc_store->askpass_callback); | ||
| 140 | |||
| 141 | if(vc_store->use_keyfile) { | ||
| 142 | r=SSL_CTX_use_PrivateKey_file(ctx, vc_store->use_keyfile, | ||
| 143 | SSL_FILETYPE_PEM); | ||
| 144 | } else if(vc_store->use_key) | ||
| 145 | r=SSL_CTX_use_PrivateKey(ctx, vc_store->use_key); | ||
| 146 | |||
| 147 | if( r!=1 || !SSL_CTX_check_private_key(ctx)) | ||
| 148 | VC_SETCERT_ERR_EXIT(store, ctx, "Load private key failed"); | ||
| 149 | } | ||
| 150 | |||
| 151 | SSL_CTX_set_app_data(ctx, vc_store); | ||
| 152 | return(ctx); | ||
| 153 | } | ||
| 154 | |||
| 155 | int vc_connect_ssl( BIO **conn, vc_x509store_t *vc_store ) | ||
| 156 | { | ||
| 157 | SSL_CTX * ctx = vc_create_sslctx(vc_store); | ||
| 158 | X509 *peercert = NULL; | ||
| 159 | BIO *ssl_conn = NULL; | ||
| 160 | const SSL *sslp = NULL; | ||
| 161 | const SSL_CIPHER * cipher = NULL; | ||
| 162 | |||
| 163 | /* To display and check server fingerprint */ | ||
| 164 | char fingerprint[EVP_MAX_MD_SIZE*4]; | ||
| 165 | unsigned char fingerprint_bin[EVP_MAX_MD_SIZE]; | ||
| 166 | unsigned int fingerprint_len; | ||
| 167 | |||
| 168 | FILE *fingerprint_file = NULL; | ||
| 169 | char * fp = fingerprint; | ||
| 170 | |||
| 171 | long result, j; | ||
| 172 | |||
| 173 | if( !ctx ) | ||
| 174 | return 1; | ||
| 175 | |||
| 176 | ssl_conn = BIO_new_ssl(ctx, 1); | ||
| 177 | SSL_CTX_free(ctx); | ||
| 178 | |||
| 179 | if( !ssl_conn ) | ||
| 180 | goto ssl_error; | ||
| 181 | |||
| 182 | BIO_push( ssl_conn, *conn ); | ||
| 183 | *conn = ssl_conn; | ||
| 184 | fflush(stdout); | ||
| 185 | |||
| 186 | if( BIO_do_handshake( *conn ) <= 0 ) | ||
| 187 | goto ssl_error; | ||
| 188 | |||
| 189 | /* Show information about cipher used */ | ||
| 190 | /* Get cipher object */ | ||
| 191 | BIO_get_ssl(ssl_conn, &sslp); | ||
| 192 | if (sslp) | ||
| 193 | cipher = SSL_get_current_cipher(sslp); | ||
| 194 | if (cipher) { | ||
| 195 | char cipher_desc[TMPSTRSIZE]; | ||
| 196 | snprintf(tmpstr, TMPSTRSIZE, "[SSL CIPHER ] %s", SSL_CIPHER_description(cipher, cipher_desc, TMPSTRSIZE)); | ||
| 197 | writecf(FS_SERV, tmpstr); | ||
| 198 | } else { | ||
| 199 | snprintf(tmpstr, TMPSTRSIZE, "[SSL ERROR ] Cipher not known / SSL object can't be queried!"); | ||
| 200 | writecf(FS_ERR, tmpstr); | ||
| 201 | } | ||
| 202 | |||
| 203 | /* Accept being connected, _if_ verification passed */ | ||
| 204 | if (!sslp) | ||
| 205 | goto ssl_error; | ||
| 206 | |||
| 207 | peercert = SSL_get_peer_certificate(sslp); | ||
| 208 | if (!peercert) | ||
| 209 | goto ssl_error; | ||
| 210 | |||
| 211 | /* show basic information about peer cert */ | ||
| 212 | snprintf(tmpstr, TMPSTRSIZE, "[SSL SUBJECT ] %s", X509_NAME_oneline(X509_get_subject_name(peercert),0,0)); | ||
| 213 | writecf(FS_SERV, tmpstr); | ||
| 214 | snprintf(tmpstr, TMPSTRSIZE, "[SSL ISSUER ] %s", X509_NAME_oneline(X509_get_issuer_name(peercert),0,0)); | ||
| 215 | writecf(FS_SERV, tmpstr); | ||
| 216 | |||
| 217 | /* calculate fingerprint */ | ||
| 218 | if (!X509_digest(peercert,EVP_sha1(),fingerprint_bin,&fingerprint_len)) | ||
| 219 | goto ssl_error; | ||
| 220 | |||
| 221 | assert ( ( fingerprint_len > 1 ) && (fingerprint_len <= EVP_MAX_MD_SIZE )); | ||
| 222 | for (j=0; j<(int)fingerprint_len; j++) | ||
| 223 | fp += sprintf(fp, "%02X:", fingerprint_bin[j]); | ||
| 224 | assert ( fp > fingerprint ); | ||
| 225 | fp[-1] = 0; | ||
| 226 | snprintf(tmpstr, TMPSTRSIZE, "[SSL FINGERPRINT ] %s (from server)", fingerprint); | ||
| 227 | writecf(FS_SERV, tmpstr); | ||
| 228 | |||
| 229 | /* we don't need the peercert anymore */ | ||
| 230 | X509_free(peercert); | ||
| 231 | |||
| 232 | /* verify fingerprint */ | ||
| 233 | if (getintoption(CF_PINFINGER)) { | ||
| 234 | |||
| 235 | fingerprint_file = fopen(tilde_expand(getstroption(CF_FINGERPRINT)), "r"); | ||
| 236 | if (fingerprint_file) { | ||
| 237 | |||
| 238 | /* Read fingerprint from file */ | ||
| 239 | char old_fingerprint[EVP_MAX_MD_SIZE*4]; | ||
| 240 | char * r = fgets(old_fingerprint, sizeof(old_fingerprint), fingerprint_file); | ||
| 241 | fclose(fingerprint_file); | ||
| 242 | |||
| 243 | if (r) { | ||
| 244 | /* chomp */ | ||
| 245 | char *nl = strchr(r, '\n'); | ||
| 246 | if (nl) *nl = 0; | ||
| 247 | |||
| 248 | /* verify fingerprint matches stored version */ | ||
| 249 | if (!strcmp(fingerprint, old_fingerprint)) | ||
| 250 | return 0; | ||
| 251 | } | ||
| 252 | |||
| 253 | snprintf(tmpstr, TMPSTRSIZE, "[SSL FINGERPRINT ] %s (from %s)", r ? old_fingerprint : "<FILE READ ERROR>", getstroption(CF_FINGERPRINT)); | ||
| 254 | writecf(FS_ERR, tmpstr); | ||
| 255 | writecf(FS_ERR, "[SSL CONNECT ERROR] Fingerprint mismatch! Server cert updated?"); | ||
| 256 | return 1; | ||
| 257 | } | ||
| 258 | |||
| 259 | fingerprint_file = fopen(tilde_expand(getstroption(CF_FINGERPRINT)), "w"); | ||
| 260 | if (!fingerprint_file) { | ||
| 261 | snprintf (tmpstr, TMPSTRSIZE, "[WARNING] Can't write fingerprint file, %s.", strerror(errno)); | ||
| 262 | writecf(FS_ERR, tmpstr); | ||
| 263 | } else { | ||
| 264 | fputs(fingerprint, fingerprint_file); | ||
| 265 | fclose(fingerprint_file); | ||
| 266 | writecf(FS_SERV, "Stored fingerprint."); | ||
| 267 | } | ||
| 268 | return 0; | ||
| 269 | } | ||
| 270 | |||
| 271 | /* If verify of x509 chain was requested, do the check here */ | ||
| 272 | result = SSL_get_verify_result(sslp); | ||
| 273 | |||
| 274 | if (result == X509_V_OK) | ||
| 275 | return 0; | ||
| 276 | |||
| 277 | if (getintoption(CF_IGNSSL)) { | ||
| 278 | writecf(FS_ERR, "[SSL VERIFY ERROR ] FAILURE IGNORED!!!"); | ||
| 279 | return 0; | ||
| 280 | } | ||
| 281 | |||
| 282 | ssl_error: | ||
| 283 | snprintf(tmpstr, TMPSTRSIZE, "[SSL CONNECT ERROR] %s", ERR_error_string (ERR_get_error (), NULL)); | ||
| 284 | writecf(FS_ERR, tmpstr); | ||
| 285 | |||
| 286 | return 1; | ||
| 287 | } | ||
| 288 | |||
| 289 | #define VC_STORE_ERR_EXIT(s) do { \ | ||
| 290 | fprintf(stderr, "[E] SSL_STORE: %s\n", ERR_error_string (ERR_get_error (), NULL)); \ | ||
| 291 | if(s) X509_STORE_free(s); \ | ||
| 292 | return(0); \ | ||
| 293 | } while(0) | ||
| 294 | |||
| 295 | X509_STORE *vc_x509store_create(vc_x509store_t *vc_store) | ||
| 296 | { | ||
| 297 | int i = 0; | ||
| 298 | int n = 0; | ||
| 299 | X509_STORE *store = NULL; | ||
| 300 | X509_LOOKUP *lookup = NULL; | ||
| 301 | |||
| 302 | store = X509_STORE_new(); | ||
| 303 | |||
| 304 | if(vc_store->callback) | ||
| 305 | X509_STORE_set_verify_cb_func(store, vc_store->callback); | ||
| 306 | else | ||
| 307 | X509_STORE_set_verify_cb_func(store, vc_verify_callback); | ||
| 308 | |||
| 309 | if( !(lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file())) ) | ||
| 310 | VC_STORE_ERR_EXIT(store); | ||
| 311 | |||
| 312 | if(!vc_store->cafile) { | ||
| 313 | if( !(vc_store->flags & VC_X509S_NODEF_CAFILE) ) | ||
| 314 | X509_LOOKUP_load_file(lookup, 0, X509_FILETYPE_DEFAULT); | ||
| 315 | } else if( !X509_LOOKUP_load_file(lookup, vc_store->cafile, | ||
| 316 | X509_FILETYPE_PEM) ) | ||
| 317 | VC_STORE_ERR_EXIT(store); | ||
| 318 | |||
| 319 | if(vc_store->crlfile) { | ||
| 320 | if( !X509_load_crl_file(lookup, vc_store->crlfile, | ||
| 321 | X509_FILETYPE_PEM) ) | ||
| 322 | VC_STORE_ERR_EXIT(store); | ||
| 323 | |||
| 324 | X509_STORE_set_flags( store, | ||
| 325 | X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL ); | ||
| 326 | } | ||
| 327 | |||
| 328 | if( !(lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir())) ) | ||
| 329 | VC_STORE_ERR_EXIT(store); | ||
| 330 | |||
| 331 | if( !vc_store->capath ) { | ||
| 332 | if( !(vc_store->flags & VC_X509S_NODEF_CAPATH) ) | ||
| 333 | X509_LOOKUP_add_dir(lookup, 0, X509_FILETYPE_DEFAULT); | ||
| 334 | } else if( !X509_LOOKUP_add_dir(lookup, vc_store->capath, | ||
| 335 | X509_FILETYPE_PEM) ) | ||
| 336 | VC_STORE_ERR_EXIT(store); | ||
| 337 | |||
| 338 | for( i=0, n=sk_X509_num(vc_store->certs); i<n; i++) | ||
| 339 | if( !X509_STORE_add_cert(store, sk_X509_value(vc_store->certs, i)) ) | ||
| 340 | VC_STORE_ERR_EXIT(store); | ||
| 341 | |||
| 342 | for( i=0, n=sk_X509_CRL_num(vc_store->crls); i<n; i++) | ||
| 343 | if( !X509_STORE_add_crl(store, | ||
| 344 | sk_X509_CRL_value(vc_store->crls, i)) ) | ||
| 345 | VC_STORE_ERR_EXIT(store); | ||
| 346 | |||
| 347 | return(store); | ||
| 348 | } | ||
| 349 | |||
| 350 | int vc_verify_callback(int ok, X509_STORE_CTX *store) | ||
| 351 | { | ||
| 352 | if(!ok) { | ||
| 353 | snprintf(tmpstr, TMPSTRSIZE, "[SSL VERIFY ERROR ] %s", | ||
| 354 | X509_verify_cert_error_string(X509_STORE_CTX_get_error(store))); | ||
| 355 | writecf(FS_ERR, tmpstr); | ||
| 356 | } | ||
| 357 | return (ok | getintoption(CF_IGNSSL)); | ||
| 358 | } | ||
| 359 | |||
| 360 | void vc_x509store_setflags(vc_x509store_t *store, int flags) | ||
| 361 | { | ||
| 362 | store->flags |= flags; | ||
| 363 | } | ||
| 364 | |||
| 365 | void vc_x509store_clearflags(vc_x509store_t *store, int flags) | ||
| 366 | { | ||
| 367 | store->flags &= ~flags; | ||
| 368 | } | ||
| 369 | |||
| 370 | void vc_x509store_setcb(vc_x509store_t *store, | ||
| 371 | vc_x509verify_cb_t callback) | ||
| 372 | { | ||
| 373 | store->callback = callback; | ||
| 374 | } | ||
| 375 | |||
| 376 | void vc_x509store_set_pkeycb(vc_x509store_t *store, | ||
| 377 | vc_askpass_cb_t callback) | ||
| 378 | { | ||
| 379 | store->askpass_callback = callback; | ||
| 380 | } | ||
| 381 | |||
| 382 | void vc_x509store_addcert(vc_x509store_t *store, X509 *cert) | ||
| 383 | { | ||
| 384 | sk_X509_push(store->certs, cert); | ||
| 385 | } | ||
| 386 | |||
| 387 | void vc_x509store_setcafile(vc_x509store_t *store, char *file) | ||
| 388 | { | ||
| 389 | free(store->cafile); | ||
| 390 | store->cafile = ( file ? strdup(file) : 0 ); | ||
| 391 | } | ||
| 392 | |||
| 393 | void vc_x509store_setcapath(vc_x509store_t *store, char *path) | ||
| 394 | { | ||
| 395 | free(store->capath); | ||
| 396 | store->capath = ( path ? strdup(path) : 0 ); | ||
| 397 | } | ||
| 398 | |||
| 399 | void vc_x509store_setcrlfile(vc_x509store_t *store, char *file) | ||
| 400 | { | ||
| 401 | free(store->crlfile); | ||
| 402 | store->crlfile = ( file ? strdup(file) : 0 ); | ||
| 403 | } | ||
| 404 | |||
| 405 | void vc_x509store_setkeyfile(vc_x509store_t *store, char *file) | ||
| 406 | { | ||
| 407 | free(store->use_keyfile); | ||
| 408 | store->use_keyfile = ( file ? strdup(file) : 0 ); | ||
| 409 | } | ||
| 410 | |||
| 411 | void vc_x509store_setcertfile(vc_x509store_t *store, char *file) | ||
| 412 | { | ||
| 413 | free(store->use_certfile); | ||
| 414 | store->use_certfile = ( file ? strdup(file) : 0 ); | ||
| 415 | } | ||
| 416 | |||
| 417 | #if 0 | ||
| 418 | int vc_tls_read() | ||
| 419 | { | ||
| 420 | |||
| 421 | } | ||
| 422 | |||
| 423 | int vc_tls_read() | ||
| 424 | { | ||
| 425 | |||
| 426 | } | ||
| 427 | #endif | ||
| 428 | |||
| 429 | vc_x509store_t *vc_init_x509store() | ||
| 430 | { | ||
| 431 | vc_x509store_t *s = malloc(sizeof(vc_x509store_t)); | ||
| 432 | if (s) { | ||
| 433 | |||
| 434 | static int sslinit; | ||
| 435 | if( !sslinit++ ) { | ||
| 436 | SSL_library_init (); | ||
| 437 | SSL_load_error_strings(); | ||
| 438 | } | ||
| 439 | |||
| 440 | s->cafile = NULL; | ||
| 441 | s->capath = NULL; | ||
| 442 | s->crlfile = NULL; | ||
| 443 | s->callback = NULL; | ||
| 444 | s->askpass_callback = NULL; | ||
| 445 | s->certs = sk_X509_new_null(); | ||
| 446 | s->crls = sk_X509_CRL_new_null(); | ||
| 447 | s->use_certfile = NULL; | ||
| 448 | s->use_certs = sk_X509_new_null(); | ||
| 449 | s->use_keyfile = NULL; | ||
| 450 | s->use_key = NULL; | ||
| 451 | s->flags = 0; | ||
| 452 | } | ||
| 453 | return s; | ||
| 454 | } | ||
| 455 | |||
| 456 | void vc_cleanup_x509store(vc_x509store_t *s) | ||
| 457 | { | ||
| 458 | free(s->cafile); | ||
| 459 | free(s->capath); | ||
| 460 | free(s->crlfile); | ||
| 461 | free(s->use_certfile); | ||
| 462 | free(s->use_keyfile); | ||
| 463 | free(s->use_key); | ||
| 464 | sk_X509_free(s->certs); | ||
| 465 | sk_X509_CRL_free(s->crls); | ||
| 466 | sk_X509_free(s->use_certs); | ||
| 467 | } | ||
| 468 | |||
| 469 | const char *vchat_ssl_version_external = "OpenSSL implementation; version unknown"; | ||
| 470 | void vchat_ssl_get_version_external() | ||
| 471 | { | ||
| 472 | char tmpstr[TMPSTRSIZE]; | ||
| 473 | snprintf(tmpstr, TMPSTRSIZE, "%s with %s", SSLeay_version(SSLEAY_VERSION), SSLeay_version(SSLEAY_CFLAGS)); | ||
| 474 | vchat_ssl_version_external = strdup(tmpstr); | ||
| 475 | } | ||
diff --git a/vchat-ssl.h b/vchat-ssl.h deleted file mode 100755 index 8dc1bfc..0000000 --- a/vchat-ssl.h +++ /dev/null | |||
| @@ -1,23 +0,0 @@ | |||
| 1 | |||
| 2 | /* prototypes */ | ||
| 3 | |||
| 4 | struct vc_x509store_t; | ||
| 5 | typedef struct vc_x509store_t vc_x509store_t; | ||
| 6 | typedef int (*vc_askpass_cb_t)(char *, int, int, void *); | ||
| 7 | |||
| 8 | vc_x509store_t *vc_init_x509store(); | ||
| 9 | void vc_x509store_set_pkeycb(vc_x509store_t *, vc_askpass_cb_t); | ||
| 10 | void vc_x509store_setflags(vc_x509store_t *, int); | ||
| 11 | void vc_x509store_setkeyfile(vc_x509store_t *, char *); | ||
| 12 | void vc_x509store_setcertfile(vc_x509store_t *, char *); | ||
| 13 | int vc_connect_ssl(BIO **conn, vc_x509store_t * ); | ||
| 14 | |||
| 15 | #define VC_X509S_NODEF_CAFILE 0x01 | ||
| 16 | #define VC_X509S_NODEF_CAPATH 0x02 | ||
| 17 | #define VC_X509S_USE_CERTIFICATE 0x04 | ||
| 18 | #define VC_X509S_SSL_VERIFY_NONE 0x10 | ||
| 19 | #define VC_X509S_SSL_VERIFY_PEER 0x20 | ||
| 20 | #define VC_X509S_SSL_VERIFY_FAIL_IF_NO_PEER_CERT 0x40 | ||
| 21 | #define VC_X509S_SSL_VERIFY_CLIENT_ONCE 0x80 | ||
| 22 | #define VC_X509S_SSL_VERIFY_MASK 0xF0 | ||
| 23 | |||
diff --git a/vchat-tls.c b/vchat-tls.c new file mode 100644 index 0000000..eaa12f4 --- /dev/null +++ b/vchat-tls.c | |||
| @@ -0,0 +1,970 @@ | |||
| 1 | /* | ||
| 2 | * vchat-client - alpha version | ||
| 3 | * vchat-tls.c - handling of SSL connection and X509 certificate | ||
| 4 | * verification | ||
| 5 | * | ||
| 6 | * Copyright (C) 2007 Thorsten Schroeder <ths@berlin.ccc.de> | ||
| 7 | * | ||
| 8 | * This program is free software. It can be redistributed and/or modified, | ||
| 9 | * provided that this copyright notice is kept intact. This program is | ||
| 10 | * distributed in the hope that it will be useful, but without any warranty; | ||
| 11 | * without even the implied warranty of merchantability or fitness for a | ||
| 12 | * particular purpose. In no event shall the copyright holder be liable for | ||
| 13 | * any direct, indirect, incidental or special damages arising in any way out | ||
| 14 | * of the use of this software. | ||
| 15 | * | ||
| 16 | */ | ||
| 17 | |||
| 18 | #include <assert.h> | ||
| 19 | #include <errno.h> | ||
| 20 | #include <stdio.h> | ||
| 21 | #include <stdlib.h> | ||
| 22 | #include <string.h> | ||
| 23 | |||
| 24 | #include <readline/readline.h> | ||
| 25 | |||
| 26 | #include "vchat-tls.h" | ||
| 27 | #include "vchat.h" | ||
| 28 | |||
| 29 | const char *vchat_tls_version = | ||
| 30 | "vchat-tls.c $Id$"; | ||
| 31 | |||
| 32 | /* Helpers to work with vc_x509store_t used by all tls libs */ | ||
| 33 | void vc_cleanup_x509store(vc_x509store_t *store) { | ||
| 34 | free(store->cafile); | ||
| 35 | free(store->capath); | ||
| 36 | free(store->crlfile); | ||
| 37 | free(store->certfile); | ||
| 38 | free(store->keyfile); | ||
| 39 | memset(store, 0, sizeof(vc_x509store_t)); | ||
| 40 | } | ||
| 41 | |||
| 42 | void vc_x509store_setflags(vc_x509store_t *store, int flags) { | ||
| 43 | store->flags |= flags; | ||
| 44 | } | ||
| 45 | void vc_x509store_clearflags(vc_x509store_t *store, int flags) { | ||
| 46 | store->flags &= ~flags; | ||
| 47 | } | ||
| 48 | void vc_x509store_set_pkeycb(vc_x509store_t *store, vc_askpass_cb_t callback) { | ||
| 49 | store->askpass_callback = callback; | ||
| 50 | } | ||
| 51 | |||
| 52 | void vc_x509store_setcafile(vc_x509store_t *store, char *file) { | ||
| 53 | free(store->cafile); | ||
| 54 | store->cafile = (file ? strdup(file) : 0); | ||
| 55 | store->flags |= VC_X509S_USE_CAFILE; | ||
| 56 | } | ||
| 57 | |||
| 58 | void vc_x509store_setcapath(vc_x509store_t *store, char *path) { | ||
| 59 | free(store->capath); | ||
| 60 | store->capath = (path ? strdup(path) : 0); | ||
| 61 | } | ||
| 62 | |||
| 63 | void vc_x509store_setcrlfile(vc_x509store_t *store, char *file) { | ||
| 64 | free(store->crlfile); | ||
| 65 | store->crlfile = (file ? strdup(file) : 0); | ||
| 66 | } | ||
| 67 | |||
| 68 | void vc_x509store_setkeyfile(vc_x509store_t *store, char *file) { | ||
| 69 | free(store->keyfile); | ||
| 70 | store->keyfile = (file ? strdup(file) : 0); | ||
| 71 | } | ||
| 72 | |||
| 73 | void vc_x509store_setcertfile(vc_x509store_t *store, char *file) { | ||
| 74 | free(store->certfile); | ||
| 75 | store->certfile = (file ? strdup(file) : 0); | ||
| 76 | store->flags |= VC_X509S_USE_CERTIFICATE; | ||
| 77 | } | ||
| 78 | |||
| 79 | static int verify_or_store_fingerprint(const char *fingerprint) { | ||
| 80 | char *fingerprint_file_path = tilde_expand(getstroption(CF_FINGERPRINT)); | ||
| 81 | if (!fingerprint_file_path) { | ||
| 82 | writecf(FS_ERR, "Error: The CF_FINGERPRINT path is not set but " | ||
| 83 | "CF_PINFINGER was requested."); | ||
| 84 | return -1; | ||
| 85 | } | ||
| 86 | |||
| 87 | FILE *fingerprint_file = fopen(fingerprint_file_path, "r"); | ||
| 88 | if (fingerprint_file) { | ||
| 89 | /* Read fingerprint from file */ | ||
| 90 | char old_fingerprint[128]; | ||
| 91 | char *r = fgets(old_fingerprint, sizeof(old_fingerprint), fingerprint_file); | ||
| 92 | fclose(fingerprint_file); | ||
| 93 | |||
| 94 | if (r) { | ||
| 95 | /* chomp */ | ||
| 96 | char *nl = strchr(r, '\n'); | ||
| 97 | if (nl) | ||
| 98 | *nl = 0; | ||
| 99 | |||
| 100 | /* verify fingerprint matches stored version */ | ||
| 101 | if (!strcmp(fingerprint, old_fingerprint)) { | ||
| 102 | writecf(FS_SERV, "[FINGERPRINT MATCH ]"); | ||
| 103 | goto cleanup_happy; | ||
| 104 | } | ||
| 105 | } | ||
| 106 | |||
| 107 | snprintf(tmpstr, TMPSTRSIZE, | ||
| 108 | "Error: Found pinned fingerprint (in %s) %s but expected %s", | ||
| 109 | r ? old_fingerprint : "<FILE READ ERROR>", | ||
| 110 | getstroption(CF_FINGERPRINT), fingerprint); | ||
| 111 | writecf(FS_ERR, tmpstr); | ||
| 112 | writecf(FS_ERR, "Error: Fingerprint mismatch! Server cert updated?"); | ||
| 113 | free(fingerprint_file_path); | ||
| 114 | return 1; | ||
| 115 | } else | ||
| 116 | writecf(FS_ERR, | ||
| 117 | "Warning: No pinned fingerprint found, writing the current one."); | ||
| 118 | |||
| 119 | fingerprint_file = fopen(fingerprint_file_path, "w"); | ||
| 120 | if (!fingerprint_file) { | ||
| 121 | snprintf(tmpstr, TMPSTRSIZE, "Warning: Can't write fingerprint file, %s.", | ||
| 122 | strerror(errno)); | ||
| 123 | writecf(FS_ERR, tmpstr); | ||
| 124 | } else { | ||
| 125 | fputs(fingerprint, fingerprint_file); | ||
| 126 | fclose(fingerprint_file); | ||
| 127 | writecf(FS_SERV, "[FINGERPRINT STORED ]"); | ||
| 128 | } | ||
| 129 | cleanup_happy: | ||
| 130 | free(fingerprint_file_path); | ||
| 131 | return 0; | ||
| 132 | } | ||
| 133 | |||
| 134 | #ifdef TLS_LIB_OPENSSL | ||
| 135 | |||
| 136 | #include <openssl/bio.h> | ||
| 137 | #include <openssl/conf.h> | ||
| 138 | #include <openssl/err.h> | ||
| 139 | #include <openssl/evp.h> | ||
| 140 | #include <openssl/ssl.h> | ||
| 141 | #include <openssl/x509.h> | ||
| 142 | #include <openssl/x509v3.h> | ||
| 143 | |||
| 144 | char *vc_openssl_version() { | ||
| 145 | snprintf(tmpstr, sizeof(tmpstr), "OpenSSL %s with %s", | ||
| 146 | SSLeay_version(SSLEAY_VERSION), SSLeay_version(SSLEAY_CFLAGS)); | ||
| 147 | return strdup(tmpstr); | ||
| 148 | } | ||
| 149 | |||
| 150 | void vc_openssl_init_x509store(vc_x509store_t *store) { | ||
| 151 | static int sslinit; | ||
| 152 | if (!sslinit++) { | ||
| 153 | SSL_library_init(); | ||
| 154 | SSL_load_error_strings(); | ||
| 155 | } | ||
| 156 | memset(store, 0, sizeof(vc_x509store_t)); | ||
| 157 | |||
| 158 | /* We want to make verifying the peer the default */ | ||
| 159 | store->flags |= VC_X509S_SSL_VERIFY_PEER; | ||
| 160 | } | ||
| 161 | |||
| 162 | /* connection BIO for openssl */ | ||
| 163 | static BIO *server_conn = NULL; | ||
| 164 | |||
| 165 | static SSL_CTX *vc_create_sslctx(vc_x509store_t *vc_store); | ||
| 166 | static int vc_verify_callback(int, X509_STORE_CTX *); | ||
| 167 | static X509_STORE *vc_x509store_create(vc_x509store_t *); | ||
| 168 | |||
| 169 | static SSL_CTX *vc_create_sslctx(vc_x509store_t *vc_store) { | ||
| 170 | int flags = 0; | ||
| 171 | |||
| 172 | /* Explicitly use TLSv1 (or maybe later) */ | ||
| 173 | SSL_CTX *ctx = SSL_CTX_new(TLS_client_method()); | ||
| 174 | X509_STORE *store = vc_x509store_create(vc_store); | ||
| 175 | |||
| 176 | if (!ctx || !store) { | ||
| 177 | snprintf(tmpstr, sizeof(tmpstr), "CREATE CTX: %s", | ||
| 178 | ERR_error_string(ERR_get_error(), NULL)); | ||
| 179 | writecf(FS_ERR, tmpstr); | ||
| 180 | if (store) | ||
| 181 | X509_STORE_free(store); | ||
| 182 | if (ctx) | ||
| 183 | SSL_CTX_free(ctx); | ||
| 184 | return NULL; | ||
| 185 | } | ||
| 186 | |||
| 187 | SSL_CTX_set_cert_store(ctx, store); | ||
| 188 | |||
| 189 | /* Disable some insecure protocols explicitly */ | ||
| 190 | SSL_CTX_set_options(ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); | ||
| 191 | if (getstroption(CF_CIPHERSUITE)) | ||
| 192 | SSL_CTX_set_cipher_list(ctx, getstroption(CF_CIPHERSUITE)); | ||
| 193 | else | ||
| 194 | SSL_CTX_set_cipher_list(ctx, | ||
| 195 | "ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA"); | ||
| 196 | |||
| 197 | SSL_CTX_set_verify_depth(ctx, getintoption(CF_VERIFYSSL)); | ||
| 198 | |||
| 199 | if (!(vc_store->flags & VC_X509S_SSL_VERIFY_MASK)) { | ||
| 200 | writecf(FS_DBG, tmpstr); | ||
| 201 | flags = SSL_VERIFY_NONE; | ||
| 202 | } else { | ||
| 203 | if (vc_store->flags & VC_X509S_SSL_VERIFY_PEER) | ||
| 204 | flags |= SSL_VERIFY_PEER; | ||
| 205 | if (vc_store->flags & VC_X509S_SSL_VERIFY_FAIL_IF_NO_PEER_CERT) | ||
| 206 | flags |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; | ||
| 207 | if (vc_store->flags & VC_X509S_SSL_VERIFY_CLIENT_ONCE) | ||
| 208 | flags |= SSL_VERIFY_CLIENT_ONCE; | ||
| 209 | } | ||
| 210 | |||
| 211 | SSL_CTX_set_verify(ctx, flags, vc_verify_callback); | ||
| 212 | |||
| 213 | if (vc_store->flags & VC_X509S_USE_CERTIFICATE) { | ||
| 214 | int r = 0; | ||
| 215 | if (vc_store->certfile) | ||
| 216 | SSL_CTX_use_certificate_chain_file(ctx, vc_store->certfile); | ||
| 217 | |||
| 218 | SSL_CTX_set_default_passwd_cb(ctx, vc_store->askpass_callback); | ||
| 219 | |||
| 220 | if (vc_store->keyfile) | ||
| 221 | r = SSL_CTX_use_PrivateKey_file(ctx, vc_store->keyfile, SSL_FILETYPE_PEM); | ||
| 222 | |||
| 223 | if (r != 1 || !SSL_CTX_check_private_key(ctx)) { | ||
| 224 | snprintf(tmpstr, sizeof(tmpstr), "CREATE CTX: Load private key failed"); | ||
| 225 | writecf(FS_ERR, tmpstr); | ||
| 226 | SSL_CTX_free(ctx); | ||
| 227 | return NULL; | ||
| 228 | } | ||
| 229 | } | ||
| 230 | |||
| 231 | SSL_CTX_set_app_data(ctx, vc_store); | ||
| 232 | return (ctx); | ||
| 233 | } | ||
| 234 | |||
| 235 | int vc_openssl_connect(int serverfd, vc_x509store_t *vc_store) { | ||
| 236 | SSL_CTX *ctx = vc_create_sslctx(vc_store); | ||
| 237 | X509 *peercert = NULL; | ||
| 238 | BIO *ssl_conn = NULL; | ||
| 239 | const SSL *sslp = NULL; | ||
| 240 | const SSL_CIPHER *cipher = NULL; | ||
| 241 | |||
| 242 | server_conn = BIO_new_socket(serverfd, 1); | ||
| 243 | |||
| 244 | /* To display and check server fingerprint */ | ||
| 245 | char fingerprint[EVP_MAX_MD_SIZE * 4]; | ||
| 246 | unsigned char fingerprint_bin[EVP_MAX_MD_SIZE]; | ||
| 247 | unsigned int fingerprint_len; | ||
| 248 | |||
| 249 | char *fp = fingerprint; | ||
| 250 | |||
| 251 | long j; | ||
| 252 | |||
| 253 | writecf(FS_SERV, "[SOCKET CONNECTED ]"); | ||
| 254 | writecf(FS_SERV, "[UPGRADING TO TLS ]"); | ||
| 255 | writecf(FS_SERV, "[TLS ENGINE OPENSSL ]"); | ||
| 256 | |||
| 257 | if (!ctx) | ||
| 258 | goto all_errors; | ||
| 259 | |||
| 260 | ssl_conn = BIO_new_ssl(ctx, 1); | ||
| 261 | SSL_CTX_free(ctx); | ||
| 262 | |||
| 263 | if (!ssl_conn) | ||
| 264 | goto ssl_error; | ||
| 265 | |||
| 266 | BIO_push(ssl_conn, server_conn); | ||
| 267 | server_conn = ssl_conn; | ||
| 268 | fflush(stdout); | ||
| 269 | |||
| 270 | if (BIO_do_handshake(server_conn) <= 0) | ||
| 271 | goto ssl_error; | ||
| 272 | |||
| 273 | /* Show information about cipher used */ | ||
| 274 | /* Get cipher object */ | ||
| 275 | BIO_get_ssl(ssl_conn, &sslp); | ||
| 276 | if (!sslp) | ||
| 277 | goto ssl_error; | ||
| 278 | |||
| 279 | cipher = SSL_get_current_cipher(sslp); | ||
| 280 | if (cipher) { | ||
| 281 | char cipher_desc[TMPSTRSIZE]; | ||
| 282 | snprintf(tmpstr, TMPSTRSIZE, "[SSL CIPHER ] %s", | ||
| 283 | SSL_CIPHER_description(cipher, cipher_desc, TMPSTRSIZE)); | ||
| 284 | writecf(FS_SERV, tmpstr); | ||
| 285 | } else { | ||
| 286 | snprintf(tmpstr, TMPSTRSIZE, | ||
| 287 | "[SSL ERROR ] Cipher not known / SSL object can't be " | ||
| 288 | "queried!"); | ||
| 289 | writecf(FS_ERR, tmpstr); | ||
| 290 | } | ||
| 291 | |||
| 292 | /* Accept being connected, _if_ verification passed */ | ||
| 293 | peercert = SSL_get_peer_certificate(sslp); | ||
| 294 | if (!peercert) | ||
| 295 | goto ssl_error; | ||
| 296 | |||
| 297 | /* show basic information about peer cert */ | ||
| 298 | snprintf(tmpstr, TMPSTRSIZE, "[SSL SUBJECT ] %s", | ||
| 299 | X509_NAME_oneline(X509_get_subject_name(peercert), 0, 0)); | ||
| 300 | writecf(FS_SERV, tmpstr); | ||
| 301 | snprintf(tmpstr, TMPSTRSIZE, "[SSL ISSUER ] %s", | ||
| 302 | X509_NAME_oneline(X509_get_issuer_name(peercert), 0, 0)); | ||
| 303 | writecf(FS_SERV, tmpstr); | ||
| 304 | |||
| 305 | /* calculate fingerprint */ | ||
| 306 | if (!X509_digest(peercert, EVP_sha1(), fingerprint_bin, &fingerprint_len)) | ||
| 307 | goto ssl_error; | ||
| 308 | |||
| 309 | assert((fingerprint_len > 1) && (fingerprint_len <= EVP_MAX_MD_SIZE)); | ||
| 310 | for (j = 0; j < (int)fingerprint_len; j++) | ||
| 311 | fp += sprintf(fp, "%02X:", fingerprint_bin[j]); | ||
| 312 | assert(fp > fingerprint); | ||
| 313 | fp[-1] = 0; | ||
| 314 | snprintf(tmpstr, TMPSTRSIZE, "[SSL FINGERPRINT ] %s (from server)", | ||
| 315 | fingerprint); | ||
| 316 | writecf(FS_SERV, tmpstr); | ||
| 317 | |||
| 318 | /* we don't need the peercert anymore */ | ||
| 319 | X509_free(peercert); | ||
| 320 | |||
| 321 | if (getintoption(CF_PINFINGER) && verify_or_store_fingerprint(fingerprint)) | ||
| 322 | return 1; | ||
| 323 | |||
| 324 | /* If verify of x509 chain was requested, do the check here */ | ||
| 325 | if (X509_V_OK == SSL_get_verify_result(sslp)) | ||
| 326 | return 0; | ||
| 327 | |||
| 328 | if (getintoption(CF_IGNSSL)) { | ||
| 329 | writecf(FS_ERR, "[SSL VERIFY ERROR ] FAILURE IGNORED!!!"); | ||
| 330 | return 0; | ||
| 331 | } | ||
| 332 | |||
| 333 | ssl_error: | ||
| 334 | snprintf(tmpstr, TMPSTRSIZE, "[SSL CONNECT ERROR ] %s", | ||
| 335 | ERR_error_string(ERR_get_error(), NULL)); | ||
| 336 | writecf(FS_ERR, tmpstr); | ||
| 337 | all_errors: | ||
| 338 | BIO_free_all(server_conn); | ||
| 339 | server_conn = NULL; | ||
| 340 | return 1; | ||
| 341 | } | ||
| 342 | |||
| 343 | #define VC_STORE_ERR_EXIT(s) \ | ||
| 344 | do { \ | ||
| 345 | fprintf(stderr, "[E] SSL_STORE: %s\n", \ | ||
| 346 | ERR_error_string(ERR_get_error(), NULL)); \ | ||
| 347 | if (s) \ | ||
| 348 | X509_STORE_free(s); \ | ||
| 349 | return (0); \ | ||
| 350 | } while (0) | ||
| 351 | |||
| 352 | X509_STORE *vc_x509store_create(vc_x509store_t *vc_store) { | ||
| 353 | X509_STORE *store = NULL; | ||
| 354 | X509_LOOKUP *lookup = NULL; | ||
| 355 | |||
| 356 | store = X509_STORE_new(); | ||
| 357 | |||
| 358 | X509_STORE_set_verify_cb_func(store, vc_verify_callback); | ||
| 359 | |||
| 360 | if (!(lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()))) | ||
| 361 | VC_STORE_ERR_EXIT(store); | ||
| 362 | |||
| 363 | if (!vc_store->cafile) { | ||
| 364 | if (!(vc_store->flags & VC_X509S_USE_CAFILE)) | ||
| 365 | X509_LOOKUP_load_file(lookup, 0, X509_FILETYPE_DEFAULT); | ||
| 366 | } else if (!X509_LOOKUP_load_file(lookup, vc_store->cafile, | ||
| 367 | X509_FILETYPE_PEM)) | ||
| 368 | VC_STORE_ERR_EXIT(store); | ||
| 369 | |||
| 370 | if (vc_store->crlfile) { | ||
| 371 | if (!X509_load_crl_file(lookup, vc_store->crlfile, X509_FILETYPE_PEM)) | ||
| 372 | VC_STORE_ERR_EXIT(store); | ||
| 373 | |||
| 374 | X509_STORE_set_flags(store, | ||
| 375 | X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL); | ||
| 376 | } | ||
| 377 | |||
| 378 | if (!(lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir()))) | ||
| 379 | VC_STORE_ERR_EXIT(store); | ||
| 380 | |||
| 381 | if (!vc_store->capath) { | ||
| 382 | if (!(vc_store->flags & VC_X509S_USE_CAPATH)) | ||
| 383 | X509_LOOKUP_add_dir(lookup, 0, X509_FILETYPE_DEFAULT); | ||
| 384 | } else if (!X509_LOOKUP_add_dir(lookup, vc_store->capath, X509_FILETYPE_PEM)) | ||
| 385 | VC_STORE_ERR_EXIT(store); | ||
| 386 | |||
| 387 | return (store); | ||
| 388 | } | ||
| 389 | |||
| 390 | int vc_verify_callback(int ok, X509_STORE_CTX *store) { | ||
| 391 | if (!ok) { | ||
| 392 | snprintf(tmpstr, TMPSTRSIZE, "[SSL VERIFY ERROR ] %s", | ||
| 393 | X509_verify_cert_error_string(X509_STORE_CTX_get_error(store))); | ||
| 394 | writecf(FS_ERR, tmpstr); | ||
| 395 | } | ||
| 396 | return (ok | getintoption(CF_IGNSSL)); | ||
| 397 | } | ||
| 398 | |||
| 399 | ssize_t vc_openssl_sendmessage(const void *buf, size_t size) { | ||
| 400 | return BIO_write(server_conn, buf, size); | ||
| 401 | } | ||
| 402 | |||
| 403 | ssize_t vc_openssl_receivemessage(void *buf, size_t size) { | ||
| 404 | ssize_t received = (ssize_t)BIO_read(server_conn, buf, size); | ||
| 405 | if (received != 0) | ||
| 406 | return received; | ||
| 407 | if (BIO_should_retry(server_conn)) | ||
| 408 | return -2; | ||
| 409 | return 0; | ||
| 410 | } | ||
| 411 | |||
| 412 | void vc_openssl_cleanup() { | ||
| 413 | BIO_free_all(server_conn); | ||
| 414 | server_conn = NULL; | ||
| 415 | } | ||
| 416 | #endif | ||
| 417 | |||
| 418 | #ifdef TLS_LIB_MBEDTLS | ||
| 419 | |||
| 420 | #include "mbedtls/error.h" | ||
| 421 | #include "mbedtls/version.h" | ||
| 422 | #include <mbedtls/ctr_drbg.h> | ||
| 423 | #include <mbedtls/debug.h> | ||
| 424 | #include <mbedtls/entropy.h> | ||
| 425 | #include <mbedtls/md.h> | ||
| 426 | #include <mbedtls/net_sockets.h> | ||
| 427 | #include <mbedtls/pk.h> | ||
| 428 | #include <mbedtls/ssl.h> | ||
| 429 | #include <mbedtls/x509.h> | ||
| 430 | |||
| 431 | #include <sys/socket.h> | ||
| 432 | |||
| 433 | const char *DRBG_PERS = "mbed TLS vchat client"; | ||
| 434 | #define MAX_SUITES 512 | ||
| 435 | typedef struct { | ||
| 436 | mbedtls_entropy_context _entropy; | ||
| 437 | mbedtls_ctr_drbg_context _ctr_drbg; | ||
| 438 | mbedtls_x509_crt _cacert; | ||
| 439 | mbedtls_x509_crt _cert; | ||
| 440 | mbedtls_pk_context _key; | ||
| 441 | mbedtls_ssl_context _ssl; | ||
| 442 | mbedtls_ssl_config _conf; | ||
| 443 | int ciphersuits[MAX_SUITES]; | ||
| 444 | } mbedstate; | ||
| 445 | static mbedstate _mbedtls_state; | ||
| 446 | |||
| 447 | char *vc_mbedtls_version() { | ||
| 448 | snprintf(tmpstr, sizeof(tmpstr), "%s", MBEDTLS_VERSION_STRING_FULL); | ||
| 449 | return strdup(tmpstr); | ||
| 450 | } | ||
| 451 | |||
| 452 | static int static_tcp_recv(void *ctx, unsigned char *buf, size_t len) { | ||
| 453 | return recv((int)(intptr_t)ctx, buf, len, 0); | ||
| 454 | } | ||
| 455 | static int static_tcp_send(void *ctx, const unsigned char *buf, size_t len) { | ||
| 456 | return send((int)(intptr_t)ctx, buf, len, 0); | ||
| 457 | } | ||
| 458 | static int map_openssl_suite(char *openssl_name); | ||
| 459 | void vc_mbedtls_init_x509store(vc_x509store_t *store) { | ||
| 460 | mbedtls_entropy_init(&_mbedtls_state._entropy); | ||
| 461 | mbedtls_ctr_drbg_init(&_mbedtls_state._ctr_drbg); | ||
| 462 | |||
| 463 | mbedtls_ctr_drbg_seed(&_mbedtls_state._ctr_drbg, mbedtls_entropy_func, | ||
| 464 | &_mbedtls_state._entropy, | ||
| 465 | (const unsigned char *)DRBG_PERS, sizeof(DRBG_PERS)); | ||
| 466 | memset(store, 0, sizeof(vc_x509store_t)); | ||
| 467 | |||
| 468 | /* We want to make verifying the peer the default */ | ||
| 469 | store->flags |= VC_X509S_SSL_VERIFY_PEER; | ||
| 470 | } | ||
| 471 | |||
| 472 | static void vc_tls_report_error(int error, char *message) { | ||
| 473 | size_t used = snprintf(tmpstr, sizeof(tmpstr), "Error: %s", message); | ||
| 474 | mbedtls_strerror(error, tmpstr + used, sizeof(tmpstr) - used); | ||
| 475 | writecf(FS_ERR, tmpstr); | ||
| 476 | } | ||
| 477 | |||
| 478 | int vc_mbedtls_connect(int serverfd, vc_x509store_t *vc_store) { | ||
| 479 | /* Some aliases for shorter references */ | ||
| 480 | mbedstate *s = &_mbedtls_state; | ||
| 481 | mbedtls_ssl_config *conf = &_mbedtls_state._conf; | ||
| 482 | mbedtls_ssl_context *ssl = &_mbedtls_state._ssl; | ||
| 483 | int ret, suitecount = 0; | ||
| 484 | char fingerprint[128], *token; | ||
| 485 | uint8_t digest[20]; | ||
| 486 | |||
| 487 | mbedtls_x509_crt_init(&s->_cacert); | ||
| 488 | mbedtls_x509_crt_init(&s->_cert); | ||
| 489 | mbedtls_pk_init(&s->_key); | ||
| 490 | mbedtls_ssl_init(ssl); | ||
| 491 | mbedtls_ssl_config_init(conf); | ||
| 492 | |||
| 493 | writecf(FS_SERV, "[SOCKET CONNECTED ]"); | ||
| 494 | writecf(FS_SERV, "[UPGRADING TO TLS ]"); | ||
| 495 | writecf(FS_SERV, "[TLS ENGINE MBEDTLS ]"); | ||
| 496 | |||
| 497 | if ((ret = mbedtls_ssl_config_defaults(conf, MBEDTLS_SSL_IS_CLIENT, | ||
| 498 | MBEDTLS_SSL_TRANSPORT_STREAM, | ||
| 499 | MBEDTLS_SSL_PRESET_DEFAULT)) != 0) { | ||
| 500 | vc_tls_report_error(ret, | ||
| 501 | "Can not initialise tls parameters, mbedtls reports: "); | ||
| 502 | return -1; | ||
| 503 | } | ||
| 504 | |||
| 505 | /* TODO: Always verify peer */ | ||
| 506 | mbedtls_ssl_conf_authmode(conf, getintoption(CF_IGNSSL) | ||
| 507 | ? MBEDTLS_SSL_VERIFY_OPTIONAL | ||
| 508 | : MBEDTLS_SSL_VERIFY_REQUIRED); | ||
| 509 | mbedtls_ssl_conf_rng(conf, mbedtls_ctr_drbg_random, &s->_ctr_drbg); | ||
| 510 | |||
| 511 | char *ciphers = getstroption(CF_CIPHERSUITE); | ||
| 512 | if (!ciphers) | ||
| 513 | ciphers = "ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA"; | ||
| 514 | ciphers = strdup(ciphers); | ||
| 515 | for (token = strtok(ciphers, ":"); token && suitecount < MAX_SUITES - 1; | ||
| 516 | token = strtok(NULL, ":")) { | ||
| 517 | int suite = mbedtls_ssl_get_ciphersuite_id(token); | ||
| 518 | if (!suite) | ||
| 519 | suite = map_openssl_suite(token); | ||
| 520 | if (suite) | ||
| 521 | s->ciphersuits[suitecount++] = suite; | ||
| 522 | } | ||
| 523 | s->ciphersuits[suitecount++] = 0; | ||
| 524 | free(ciphers); | ||
| 525 | |||
| 526 | mbedtls_ssl_conf_ciphersuites(conf, s->ciphersuits); | ||
| 527 | |||
| 528 | if (vc_store->cafile) { | ||
| 529 | if ((ret = mbedtls_x509_crt_parse_file(&s->_cacert, vc_store->cafile)) != | ||
| 530 | 0) { | ||
| 531 | vc_tls_report_error(ret, "Can not load CA cert, mbedtls reports: "); | ||
| 532 | return -1; | ||
| 533 | } | ||
| 534 | mbedtls_ssl_conf_ca_chain(conf, &s->_cacert, NULL); | ||
| 535 | writecf(FS_SERV, "[CA CERT LOADED ]"); | ||
| 536 | } | ||
| 537 | |||
| 538 | if (vc_store->flags & VC_X509S_USE_CERTIFICATE) { | ||
| 539 | char *password = NULL; | ||
| 540 | char password_buf[1024]; | ||
| 541 | |||
| 542 | if ((ret = mbedtls_x509_crt_parse_file(&s->_cert, vc_store->certfile)) != | ||
| 543 | 0) { | ||
| 544 | vc_tls_report_error(ret, "Can not load client cert, mbedtls reports: "); | ||
| 545 | return -1; | ||
| 546 | } | ||
| 547 | writecf(FS_SERV, "[CLIENT CERT LOADED ]"); | ||
| 548 | |||
| 549 | while (1) { | ||
| 550 | if ((ret = mbedtls_pk_parse_keyfile(&s->_key, vc_store->keyfile, password | ||
| 551 | #if MBEDTLS_VERSION_MAJOR >= 3 | ||
| 552 | , | ||
| 553 | mbedtls_ctr_drbg_random, &s->_ctr_drbg | ||
| 554 | #endif | ||
| 555 | )) == 0) | ||
| 556 | break; | ||
| 557 | if (ret != MBEDTLS_ERR_PK_PASSWORD_REQUIRED && | ||
| 558 | ret != MBEDTLS_ERR_PK_PASSWORD_MISMATCH) { | ||
| 559 | vc_tls_report_error(ret, "Can not load client key, mbedtls reports: "); | ||
| 560 | return -1; | ||
| 561 | } | ||
| 562 | if (ret == MBEDTLS_ERR_PK_PASSWORD_MISMATCH) | ||
| 563 | vc_tls_report_error(ret, "Wrong passphrase, mbedtls reports: "); | ||
| 564 | vc_store->askpass_callback(password_buf, sizeof(password_buf), 0, NULL); | ||
| 565 | password = password_buf; | ||
| 566 | } | ||
| 567 | #if defined(__linux__) || defined(__OpenBSD__) | ||
| 568 | explicit_bzero(password_buf, sizeof(password_buf)); | ||
| 569 | #else | ||
| 570 | memset_s(password_buf, sizeof(password_buf), 0, sizeof(password_buf)); | ||
| 571 | #endif | ||
| 572 | writecf(FS_SERV, "[CLIENT KEY LOADED ]"); | ||
| 573 | |||
| 574 | #if MBEDTLS_VERSION_MAJOR == 3 && MBEDTLS_VERSION_MINOR == 0 | ||
| 575 | mbedtls_pk_context *pubkey = &(s->_cert.MBEDTLS_PRIVATE(pk)); | ||
| 576 | #else | ||
| 577 | mbedtls_pk_context *pubkey = &(s->_cert.pk); | ||
| 578 | #endif | ||
| 579 | |||
| 580 | if ((ret = mbedtls_pk_check_pair(pubkey, &s->_key | ||
| 581 | #if MBEDTLS_VERSION_MAJOR >= 3 | ||
| 582 | , | ||
| 583 | mbedtls_ctr_drbg_random, &s->_ctr_drbg | ||
| 584 | #endif | ||
| 585 | )) != 0) { | ||
| 586 | vc_tls_report_error(ret, "Cert and key mismatch, mbedtls reports: "); | ||
| 587 | return 1; | ||
| 588 | } | ||
| 589 | |||
| 590 | if ((ret = mbedtls_ssl_conf_own_cert(conf, &s->_cert, &s->_key)) != 0) { | ||
| 591 | vc_tls_report_error( | ||
| 592 | ret, "Setting key and cert to tls session fails, mbedtls reports: "); | ||
| 593 | return -1; | ||
| 594 | } | ||
| 595 | } | ||
| 596 | |||
| 597 | /* Config constructed, pass to ssl */ | ||
| 598 | /* Init ssl and config structs and configure ssl ctx */ | ||
| 599 | if ((ret = mbedtls_ssl_setup(ssl, conf)) != 0) { | ||
| 600 | vc_tls_report_error( | ||
| 601 | ret, "Can not configure parameters on tls context, mbedtls reports: "); | ||
| 602 | return -1; | ||
| 603 | } | ||
| 604 | /* TODO: mbedtls_ssl_set_hostname(&ssl, SERVER_NAME) */ | ||
| 605 | |||
| 606 | mbedtls_ssl_set_bio(ssl, (void *)(intptr_t)serverfd, static_tcp_send, | ||
| 607 | static_tcp_recv, NULL); | ||
| 608 | |||
| 609 | while ((ret = mbedtls_ssl_handshake(ssl)) != 0) | ||
| 610 | if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { | ||
| 611 | vc_tls_report_error(ret, "TLS handshake failed, mbedtls reports: "); | ||
| 612 | return -1; | ||
| 613 | } | ||
| 614 | |||
| 615 | writecf(FS_SERV, "[TLS HANDSHAKE DONE ]"); | ||
| 616 | snprintf(tmpstr, TMPSTRSIZE, "[TLS CIPHER SUITE ] %s", | ||
| 617 | mbedtls_ssl_get_ciphersuite(ssl)); | ||
| 618 | writecf(FS_SERV, tmpstr); | ||
| 619 | |||
| 620 | const mbedtls_x509_crt *peer_cert = mbedtls_ssl_get_peer_cert(ssl); | ||
| 621 | mbedtls_x509_crt_info(tmpstr, sizeof(tmpstr), "[TLS PEER INFO ] ", | ||
| 622 | peer_cert); | ||
| 623 | |||
| 624 | for (token = strtok(tmpstr, "\n"); token; token = strtok(NULL, "\n")) | ||
| 625 | writecf(FS_SERV, token); | ||
| 626 | |||
| 627 | #if MBEDTLS_VERSION_MAJOR == 3 && MBEDTLS_VERSION_MINOR == 0 | ||
| 628 | const uint8_t *const rawcert_buf = | ||
| 629 | peer_cert->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(p); | ||
| 630 | size_t rawcert_len = peer_cert->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(len); | ||
| 631 | #else | ||
| 632 | const uint8_t *const rawcert_buf = peer_cert->raw.p; | ||
| 633 | size_t rawcert_len = peer_cert->raw.len; | ||
| 634 | #endif | ||
| 635 | const mbedtls_md_info_t *mdinfo = mbedtls_md_info_from_string("SHA1"); | ||
| 636 | if (mdinfo) { | ||
| 637 | mbedtls_md(mdinfo, rawcert_buf, rawcert_len, digest); | ||
| 638 | |||
| 639 | char *fp = fingerprint; | ||
| 640 | for (int j = 0; j < mbedtls_md_get_size(mdinfo); j++) | ||
| 641 | fp += sprintf(fp, "%02X:", digest[j]); | ||
| 642 | assert(fp > fingerprint); | ||
| 643 | fp[-1] = 0; | ||
| 644 | snprintf(tmpstr, TMPSTRSIZE, "[TLS FINGERPRINT ] %s (from server)", | ||
| 645 | fingerprint); | ||
| 646 | writecf(FS_SERV, tmpstr); | ||
| 647 | |||
| 648 | if (getintoption(CF_PINFINGER) && verify_or_store_fingerprint(fingerprint)) | ||
| 649 | return 1; | ||
| 650 | } else { | ||
| 651 | writecf(FS_ERR, "Warning: Unable to load SHA-1 md"); | ||
| 652 | if (getintoption(CF_PINFINGER)) { | ||
| 653 | writecf( | ||
| 654 | FS_ERR, | ||
| 655 | "ERROR: Can not compute fingerprint, but pinning check is required"); | ||
| 656 | return 1; | ||
| 657 | } | ||
| 658 | } | ||
| 659 | |||
| 660 | ret = mbedtls_ssl_get_verify_result(ssl); | ||
| 661 | switch (ret) { | ||
| 662 | case 0: | ||
| 663 | writecf(FS_SERV, "[TLS HANDSHAKE OK ]"); | ||
| 664 | break; | ||
| 665 | case -1: | ||
| 666 | writecf(FS_ERR, "Error: TLS verify for an unknown reason"); | ||
| 667 | return -1; | ||
| 668 | case MBEDTLS_X509_BADCERT_SKIP_VERIFY: | ||
| 669 | case MBEDTLS_X509_BADCERT_NOT_TRUSTED: | ||
| 670 | if (getintoption(CF_IGNSSL) || !getintoption(CF_VERIFYSSL)) | ||
| 671 | return 0; | ||
| 672 | vc_tls_report_error(ret, "TLS verify failed, mbedtls reports: "); | ||
| 673 | return -1; | ||
| 674 | default: | ||
| 675 | vc_tls_report_error(ret, "TLS verify failed, mbedtls reports: "); | ||
| 676 | return -1; | ||
| 677 | } | ||
| 678 | |||
| 679 | return 0; | ||
| 680 | } | ||
| 681 | |||
| 682 | ssize_t vc_mbedtls_sendmessage(const void *buf, size_t size) { | ||
| 683 | return mbedtls_ssl_write(&_mbedtls_state._ssl, buf, size); | ||
| 684 | } | ||
| 685 | |||
| 686 | ssize_t vc_mbedtls_receivemessage(void *buf, size_t size) { | ||
| 687 | ssize_t received = (ssize_t)mbedtls_ssl_read(&_mbedtls_state._ssl, buf, size); | ||
| 688 | switch (received) { | ||
| 689 | case MBEDTLS_ERR_SSL_WANT_READ: | ||
| 690 | case MBEDTLS_ERR_SSL_WANT_WRITE: | ||
| 691 | return -2; | ||
| 692 | case MBEDTLS_ERR_SSL_CONN_EOF: | ||
| 693 | case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY: | ||
| 694 | case 0: | ||
| 695 | return 0; | ||
| 696 | default: | ||
| 697 | if (received > 0) | ||
| 698 | return received; | ||
| 699 | return -1; | ||
| 700 | } | ||
| 701 | } | ||
| 702 | |||
| 703 | void vc_mbedtls_cleanup() { | ||
| 704 | mbedtls_x509_crt_free(&_mbedtls_state._cacert); | ||
| 705 | mbedtls_x509_crt_free(&_mbedtls_state._cert); | ||
| 706 | mbedtls_pk_free(&_mbedtls_state._key); | ||
| 707 | mbedtls_ssl_free(&_mbedtls_state._ssl); | ||
| 708 | mbedtls_ssl_config_free(&_mbedtls_state._conf); | ||
| 709 | mbedtls_ctr_drbg_free(&_mbedtls_state._ctr_drbg); | ||
| 710 | } | ||
| 711 | |||
| 712 | /* Taken from https://testssl.sh/openssl-iana.mapping.html */ | ||
| 713 | static const char * xlate_openssl[] = { | ||
| 714 | "NULL-MD5", "TLS-RSA-WITH-NULL-MD5", | ||
| 715 | "NULL-SHA", "TLS-RSA-WITH-NULL-SHA", | ||
| 716 | "EXP-RC4-MD5", "TLS-RSA-EXPORT-WITH-RC4-40-MD5", | ||
| 717 | "RC4-MD5", "TLS-RSA-WITH-RC4-128-MD5", | ||
| 718 | "RC4-SHA", "TLS-RSA-WITH-RC4-128-SHA", | ||
| 719 | "EXP-RC2-CBC-MD5", "TLS-RSA-EXPORT-WITH-RC2-CBC-40-MD5", | ||
| 720 | "IDEA-CBC-SHA", "TLS-RSA-WITH-IDEA-CBC-SHA", | ||
| 721 | "EXP-DES-CBC-SHA", "TLS-RSA-EXPORT-WITH-DES40-CBC-SHA", | ||
| 722 | "DES-CBC-SHA", "TLS-RSA-WITH-DES-CBC-SHA", | ||
| 723 | "DES-CBC3-SHA", "TLS-RSA-WITH-3DES-EDE-CBC-SHA", | ||
| 724 | "EXP-DH-DSS-DES-CBC-SHA", "TLS-DH-DSS-EXPORT-WITH-DES40-CBC-SHA", | ||
| 725 | "DH-DSS-DES-CBC-SHA", "TLS-DH-DSS-WITH-DES-CBC-SHA", | ||
| 726 | "DH-DSS-DES-CBC3-SHA", "TLS-DH-DSS-WITH-3DES-EDE-CBC-SHA", | ||
| 727 | "EXP-DH-RSA-DES-CBC-SHA", "TLS-DH-RSA-EXPORT-WITH-DES40-CBC-SHA", | ||
| 728 | "DH-RSA-DES-CBC-SHA", "TLS-DH-RSA-WITH-DES-CBC-SHA", | ||
| 729 | "DH-RSA-DES-CBC3-SHA", "TLS-DH-RSA-WITH-3DES-EDE-CBC-SHA", | ||
| 730 | "EXP-EDH-DSS-DES-CBC-SHA", "TLS-DHE-DSS-EXPORT-WITH-DES40-CBC-SHA", | ||
| 731 | "EDH-DSS-DES-CBC-SHA", "TLS-DHE-DSS-WITH-DES-CBC-SHA", | ||
| 732 | "EDH-DSS-DES-CBC3-SHA", "TLS-DHE-DSS-WITH-3DES-EDE-CBC-SHA", | ||
| 733 | "EXP-EDH-RSA-DES-CBC-SHA", "TLS-DHE-RSA-EXPORT-WITH-DES40-CBC-SHA", | ||
| 734 | "EDH-RSA-DES-CBC-SHA", "TLS-DHE-RSA-WITH-DES-CBC-SHA", | ||
| 735 | "EDH-RSA-DES-CBC3-SHA", "TLS-DHE-RSA-WITH-3DES-EDE-CBC-SHA", | ||
| 736 | "EXP-ADH-RC4-MD5", "TLS-DH-anon-EXPORT-WITH-RC4-40-MD5", | ||
| 737 | "ADH-RC4-MD5", "TLS-DH-anon-WITH-RC4-128-MD5", | ||
| 738 | "EXP-ADH-DES-CBC-SHA", "TLS-DH-anon-EXPORT-WITH-DES40-CBC-SHA", | ||
| 739 | "ADH-DES-CBC-SHA", "TLS-DH-anon-WITH-DES-CBC-SHA", | ||
| 740 | "ADH-DES-CBC3-SHA", "TLS-DH-anon-WITH-3DES-EDE-CBC-SHA", | ||
| 741 | "KRB5-DES-CBC-SHA", "TLS-KRB5-WITH-DES-CBC-SHA", | ||
| 742 | "KRB5-DES-CBC3-SHA", "TLS-KRB5-WITH-3DES-EDE-CBC-SHA", | ||
| 743 | "KRB5-RC4-SHA", "TLS-KRB5-WITH-RC4-128-SHA", | ||
| 744 | "KRB5-IDEA-CBC-SHA", "TLS-KRB5-WITH-IDEA-CBC-SHA", | ||
| 745 | "KRB5-DES-CBC-MD5", "TLS-KRB5-WITH-DES-CBC-MD5", | ||
| 746 | "KRB5-DES-CBC3-MD5", "TLS-KRB5-WITH-3DES-EDE-CBC-MD5", | ||
| 747 | "KRB5-RC4-MD5", "TLS-KRB5-WITH-RC4-128-MD5", | ||
| 748 | "KRB5-IDEA-CBC-MD5", "TLS-KRB5-WITH-IDEA-CBC-MD5", | ||
| 749 | "EXP-KRB5-DES-CBC-SHA", "TLS-KRB5-EXPORT-WITH-DES-CBC-40-SHA", | ||
| 750 | "EXP-KRB5-RC2-CBC-SHA", "TLS-KRB5-EXPORT-WITH-RC2-CBC-40-SHA", | ||
| 751 | "EXP-KRB5-RC4-SHA", "TLS-KRB5-EXPORT-WITH-RC4-40-SHA", | ||
| 752 | "EXP-KRB5-DES-CBC-MD5", "TLS-KRB5-EXPORT-WITH-DES-CBC-40-MD5", | ||
| 753 | "EXP-KRB5-RC2-CBC-MD5", "TLS-KRB5-EXPORT-WITH-RC2-CBC-40-MD5", | ||
| 754 | "EXP-KRB5-RC4-MD5", "TLS-KRB5-EXPORT-WITH-RC4-40-MD5", | ||
| 755 | "PSK-NULL-SHA", "TLS-PSK-WITH-NULL-SHA", | ||
| 756 | "DHE-PSK-NULL-SHA", "TLS-DHE-PSK-WITH-NULL-SHA", | ||
| 757 | "RSA-PSK-NULL-SHA", "TLS-RSA-PSK-WITH-NULL-SHA", | ||
| 758 | "AES128-SHA", "TLS-RSA-WITH-AES-128-CBC-SHA", | ||
| 759 | "DH-DSS-AES128-SHA", "TLS-DH-DSS-WITH-AES-128-CBC-SHA", | ||
| 760 | "DH-RSA-AES128-SHA", "TLS-DH-RSA-WITH-AES-128-CBC-SHA", | ||
| 761 | "DHE-DSS-AES128-SHA", "TLS-DHE-DSS-WITH-AES-128-CBC-SHA", | ||
| 762 | "DHE-RSA-AES128-SHA", "TLS-DHE-RSA-WITH-AES-128-CBC-SHA", | ||
| 763 | "ADH-AES128-SHA", "TLS-DH-anon-WITH-AES-128-CBC-SHA", | ||
| 764 | "AES256-SHA", "TLS-RSA-WITH-AES-256-CBC-SHA", | ||
| 765 | "DH-DSS-AES256-SHA", "TLS-DH-DSS-WITH-AES-256-CBC-SHA", | ||
| 766 | "DH-RSA-AES256-SHA", "TLS-DH-RSA-WITH-AES-256-CBC-SHA", | ||
| 767 | "DHE-DSS-AES256-SHA", "TLS-DHE-DSS-WITH-AES-256-CBC-SHA", | ||
| 768 | "DHE-RSA-AES256-SHA", "TLS-DHE-RSA-WITH-AES-256-CBC-SHA", | ||
| 769 | "ADH-AES256-SHA", "TLS-DH-anon-WITH-AES-256-CBC-SHA", | ||
| 770 | "NULL-SHA256", "TLS-RSA-WITH-NULL-SHA256", | ||
| 771 | "AES128-SHA256", "TLS-RSA-WITH-AES-128-CBC-SHA256", | ||
| 772 | "AES256-SHA256", "TLS-RSA-WITH-AES-256-CBC-SHA256", | ||
| 773 | "DH-DSS-AES128-SHA256", "TLS-DH-DSS-WITH-AES-128-CBC-SHA256", | ||
| 774 | "DH-RSA-AES128-SHA256", "TLS-DH-RSA-WITH-AES-128-CBC-SHA256", | ||
| 775 | "DHE-DSS-AES128-SHA256", "TLS-DHE-DSS-WITH-AES-128-CBC-SHA256", | ||
| 776 | "CAMELLIA128-SHA", "TLS-RSA-WITH-CAMELLIA-128-CBC-SHA", | ||
| 777 | "DH-DSS-CAMELLIA128-SHA", "TLS-DH-DSS-WITH-CAMELLIA-128-CBC-SHA", | ||
| 778 | "DH-RSA-CAMELLIA128-SHA", "TLS-DH-RSA-WITH-CAMELLIA-128-CBC-SHA", | ||
| 779 | "DHE-DSS-CAMELLIA128-SHA", "TLS-DHE-DSS-WITH-CAMELLIA-128-CBC-SHA", | ||
| 780 | "DHE-RSA-CAMELLIA128-SHA", "TLS-DHE-RSA-WITH-CAMELLIA-128-CBC-SHA", | ||
| 781 | "ADH-CAMELLIA128-SHA", "TLS-DH-anon-WITH-CAMELLIA-128-CBC-SHA", | ||
| 782 | "EXP1024-RC4-MD5", "TLS-RSA-EXPORT1024-WITH-RC4-56-MD5", | ||
| 783 | "EXP1024-RC2-CBC-MD5", "TLS-RSA-EXPORT1024-WITH-RC2-CBC-56-MD5", | ||
| 784 | "EXP1024-DES-CBC-SHA", "TLS-RSA-EXPORT1024-WITH-DES-CBC-SHA", | ||
| 785 | "EXP1024-DHE-DSS-DES-CBC-SHA", "TLS-DHE-DSS-EXPORT1024-WITH-DES-CBC-SHA", | ||
| 786 | "EXP1024-RC4-SHA", "TLS-RSA-EXPORT1024-WITH-RC4-56-SHA", | ||
| 787 | "EXP1024-DHE-DSS-RC4-SHA", "TLS-DHE-DSS-EXPORT1024-WITH-RC4-56-SHA", | ||
| 788 | "DHE-DSS-RC4-SHA", "TLS-DHE-DSS-WITH-RC4-128-SHA", | ||
| 789 | "DHE-RSA-AES128-SHA256", "TLS-DHE-RSA-WITH-AES-128-CBC-SHA256", | ||
| 790 | "DH-DSS-AES256-SHA256", "TLS-DH-DSS-WITH-AES-256-CBC-SHA256", | ||
| 791 | "DH-RSA-AES256-SHA256", "TLS-DH-RSA-WITH-AES-256-CBC-SHA256", | ||
| 792 | "DHE-DSS-AES256-SHA256", "TLS-DHE-DSS-WITH-AES-256-CBC-SHA256", | ||
| 793 | "DHE-RSA-AES256-SHA256", "TLS-DHE-RSA-WITH-AES-256-CBC-SHA256", | ||
| 794 | "ADH-AES128-SHA256", "TLS-DH-anon-WITH-AES-128-CBC-SHA256", | ||
| 795 | "ADH-AES256-SHA256", "TLS-DH-anon-WITH-AES-256-CBC-SHA256", | ||
| 796 | "GOST94-GOST89-GOST89", "TLS-GOSTR341094-WITH-28147-CNT-IMIT", | ||
| 797 | "GOST2001-GOST89-GOST89", "TLS-GOSTR341001-WITH-28147-CNT-IMIT", | ||
| 798 | "GOST94-NULL-GOST94", "TLS-GOSTR341001-WITH-NULL-GOSTR3411", | ||
| 799 | "GOST2001-GOST89-GOST89", "TLS-GOSTR341094-WITH-NULL-GOSTR3411", | ||
| 800 | "CAMELLIA256-SHA", "TLS-RSA-WITH-CAMELLIA-256-CBC-SHA", | ||
| 801 | "DH-DSS-CAMELLIA256-SHA", "TLS-DH-DSS-WITH-CAMELLIA-256-CBC-SHA", | ||
| 802 | "DH-RSA-CAMELLIA256-SHA", "TLS-DH-RSA-WITH-CAMELLIA-256-CBC-SHA", | ||
| 803 | "DHE-DSS-CAMELLIA256-SHA", "TLS-DHE-DSS-WITH-CAMELLIA-256-CBC-SHA", | ||
| 804 | "DHE-RSA-CAMELLIA256-SHA", "TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA", | ||
| 805 | "ADH-CAMELLIA256-SHA", "TLS-DH-anon-WITH-CAMELLIA-256-CBC-SHA", | ||
| 806 | "PSK-RC4-SHA", "TLS-PSK-WITH-RC4-128-SHA", | ||
| 807 | "PSK-3DES-EDE-CBC-SHA", "TLS-PSK-WITH-3DES-EDE-CBC-SHA", | ||
| 808 | "PSK-AES128-CBC-SHA", "TLS-PSK-WITH-AES-128-CBC-SHA", | ||
| 809 | "PSK-AES256-CBC-SHA", "TLS-PSK-WITH-AES-256-CBC-SHA", | ||
| 810 | "SEED-SHA", "TLS-RSA-WITH-SEED-CBC-SHA", | ||
| 811 | "DH-DSS-SEED-SHA", "TLS-DH-DSS-WITH-SEED-CBC-SHA", | ||
| 812 | "DH-RSA-SEED-SHA", "TLS-DH-RSA-WITH-SEED-CBC-SHA", | ||
| 813 | "DHE-DSS-SEED-SHA", "TLS-DHE-DSS-WITH-SEED-CBC-SHA", | ||
| 814 | "DHE-RSA-SEED-SHA", "TLS-DHE-RSA-WITH-SEED-CBC-SHA", | ||
| 815 | "ADH-SEED-SHA", "TLS-DH-anon-WITH-SEED-CBC-SHA", | ||
| 816 | "AES128-GCM-SHA256", "TLS-RSA-WITH-AES-128-GCM-SHA256", | ||
| 817 | "AES256-GCM-SHA384", "TLS-RSA-WITH-AES-256-GCM-SHA384", | ||
| 818 | "DHE-RSA-AES128-GCM-SHA256", "TLS-DHE-RSA-WITH-AES-128-GCM-SHA256", | ||
| 819 | "DHE-RSA-AES256-GCM-SHA384", "TLS-DHE-RSA-WITH-AES-256-GCM-SHA384", | ||
| 820 | "DH-RSA-AES128-GCM-SHA256", "TLS-DH-RSA-WITH-AES-128-GCM-SHA256", | ||
| 821 | "DH-RSA-AES256-GCM-SHA384", "TLS-DH-RSA-WITH-AES-256-GCM-SHA384", | ||
| 822 | "DHE-DSS-AES128-GCM-SHA256", "TLS-DHE-DSS-WITH-AES-128-GCM-SHA256", | ||
| 823 | "DHE-DSS-AES256-GCM-SHA384", "TLS-DHE-DSS-WITH-AES-256-GCM-SHA384", | ||
| 824 | "DH-DSS-AES128-GCM-SHA256", "TLS-DH-DSS-WITH-AES-128-GCM-SHA256", | ||
| 825 | "DH-DSS-AES256-GCM-SHA384", "TLS-DH-DSS-WITH-AES-256-GCM-SHA384", | ||
| 826 | "ADH-AES128-GCM-SHA256", "TLS-DH-anon-WITH-AES-128-GCM-SHA256", | ||
| 827 | "ADH-AES256-GCM-SHA384", "TLS-DH-anon-WITH-AES-256-GCM-SHA384", | ||
| 828 | "CAMELLIA128-SHA256", "TLS-RSA-WITH-CAMELLIA-128-CBC-SHA256", | ||
| 829 | "DH-DSS-CAMELLIA128-SHA256", "TLS-DH-DSS-WITH-CAMELLIA-128-CBC-SHA256", | ||
| 830 | "DH-RSA-CAMELLIA128-SHA256", "TLS-DH-RSA-WITH-CAMELLIA-128-CBC-SHA256", | ||
| 831 | "DHE-DSS-CAMELLIA128-SHA256", "TLS-DHE-DSS-WITH-CAMELLIA-128-CBC-SHA256", | ||
| 832 | "DHE-RSA-CAMELLIA128-SHA256", "TLS-DHE-RSA-WITH-CAMELLIA-128-CBC-SHA256", | ||
| 833 | "ADH-CAMELLIA128-SHA256", "TLS-DH-anon-WITH-CAMELLIA-128-CBC-SHA256", | ||
| 834 | "TLS-FALLBACK-SCSV", "TLS-EMPTY-RENEGOTIATION-INFO-SCSV", | ||
| 835 | "TLS-AES-128-GCM-SHA256", "TLS-AES-128-GCM-SHA256", | ||
| 836 | "TLS-AES-256-GCM-SHA384", "TLS-AES-256-GCM-SHA384", | ||
| 837 | "TLS-CHACHA20-POLY1305-SHA256", "TLS-CHACHA20-POLY1305-SHA256", | ||
| 838 | "TLS-AES-128-CCM-SHA256", "TLS-AES-128-CCM-SHA256", | ||
| 839 | "TLS-AES-128-CCM-8-SHA256", "TLS-AES-128-CCM-8-SHA256", | ||
| 840 | "ECDH-ECDSA-NULL-SHA", "TLS-ECDH-ECDSA-WITH-NULL-SHA", | ||
| 841 | "ECDH-ECDSA-RC4-SHA", "TLS-ECDH-ECDSA-WITH-RC4-128-SHA", | ||
| 842 | "ECDH-ECDSA-DES-CBC3-SHA", "TLS-ECDH-ECDSA-WITH-3DES-EDE-CBC-SHA", | ||
| 843 | "ECDH-ECDSA-AES128-SHA", "TLS-ECDH-ECDSA-WITH-AES-128-CBC-SHA", | ||
| 844 | "ECDH-ECDSA-AES256-SHA", "TLS-ECDH-ECDSA-WITH-AES-256-CBC-SHA", | ||
| 845 | "ECDHE-ECDSA-NULL-SHA", "TLS-ECDHE-ECDSA-WITH-NULL-SHA", | ||
| 846 | "ECDHE-ECDSA-RC4-SHA", "TLS-ECDHE-ECDSA-WITH-RC4-128-SHA", | ||
| 847 | "ECDHE-ECDSA-DES-CBC3-SHA", "TLS-ECDHE-ECDSA-WITH-3DES-EDE-CBC-SHA", | ||
| 848 | "ECDHE-ECDSA-AES128-SHA", "TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA", | ||
| 849 | "ECDHE-ECDSA-AES256-SHA", "TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA", | ||
| 850 | "ECDH-RSA-NULL-SHA", "TLS-ECDH-RSA-WITH-NULL-SHA", | ||
| 851 | "ECDH-RSA-RC4-SHA", "TLS-ECDH-RSA-WITH-RC4-128-SHA", | ||
| 852 | "ECDH-RSA-DES-CBC3-SHA", "TLS-ECDH-RSA-WITH-3DES-EDE-CBC-SHA", | ||
| 853 | "ECDH-RSA-AES128-SHA", "TLS-ECDH-RSA-WITH-AES-128-CBC-SHA", | ||
| 854 | "ECDH-RSA-AES256-SHA", "TLS-ECDH-RSA-WITH-AES-256-CBC-SHA", | ||
| 855 | "ECDHE-RSA-NULL-SHA", "TLS-ECDHE-RSA-WITH-NULL-SHA", | ||
| 856 | "ECDHE-RSA-RC4-SHA", "TLS-ECDHE-RSA-WITH-RC4-128-SHA", | ||
| 857 | "ECDHE-RSA-DES-CBC3-SHA", "TLS-ECDHE-RSA-WITH-3DES-EDE-CBC-SHA", | ||
| 858 | "ECDHE-RSA-AES128-SHA", "TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA", | ||
| 859 | "ECDHE-RSA-AES256-SHA", "TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA", | ||
| 860 | "AECDH-NULL-SHA", "TLS-ECDH-anon-WITH-NULL-SHA", | ||
| 861 | "AECDH-RC4-SHA", "TLS-ECDH-anon-WITH-RC4-128-SHA", | ||
| 862 | "AECDH-DES-CBC3-SHA", "TLS-ECDH-anon-WITH-3DES-EDE-CBC-SHA", | ||
| 863 | "AECDH-AES128-SHA", "TLS-ECDH-anon-WITH-AES-128-CBC-SHA", | ||
| 864 | "AECDH-AES256-SHA", "TLS-ECDH-anon-WITH-AES-256-CBC-SHA", | ||
| 865 | "SRP-3DES-EDE-CBC-SHA", "TLS-SRP-SHA-WITH-3DES-EDE-CBC-SHA", | ||
| 866 | "SRP-RSA-3DES-EDE-CBC-SHA", "TLS-SRP-SHA-RSA-WITH-3DES-EDE-CBC-SHA", | ||
| 867 | "SRP-DSS-3DES-EDE-CBC-SHA", "TLS-SRP-SHA-DSS-WITH-3DES-EDE-CBC-SHA", | ||
| 868 | "SRP-AES-128-CBC-SHA", "TLS-SRP-SHA-WITH-AES-128-CBC-SHA", | ||
| 869 | "SRP-RSA-AES-128-CBC-SHA", "TLS-SRP-SHA-RSA-WITH-AES-128-CBC-SHA", | ||
| 870 | "SRP-DSS-AES-128-CBC-SHA", "TLS-SRP-SHA-DSS-WITH-AES-128-CBC-SHA", | ||
| 871 | "SRP-AES-256-CBC-SHA", "TLS-SRP-SHA-WITH-AES-256-CBC-SHA", | ||
| 872 | "SRP-RSA-AES-256-CBC-SHA", "TLS-SRP-SHA-RSA-WITH-AES-256-CBC-SHA", | ||
| 873 | "SRP-DSS-AES-256-CBC-SHA", "TLS-SRP-SHA-DSS-WITH-AES-256-CBC-SHA", | ||
| 874 | "ECDHE-ECDSA-AES128-SHA256", "TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA256", | ||
| 875 | "ECDHE-ECDSA-AES256-SHA384", "TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA384", | ||
| 876 | "ECDH-ECDSA-AES128-SHA256", "TLS-ECDH-ECDSA-WITH-AES-128-CBC-SHA256", | ||
| 877 | "ECDH-ECDSA-AES256-SHA384", "TLS-ECDH-ECDSA-WITH-AES-256-CBC-SHA384", | ||
| 878 | "ECDHE-RSA-AES128-SHA256", "TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA256", | ||
| 879 | "ECDHE-RSA-AES256-SHA384", "TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA384", | ||
| 880 | "ECDH-RSA-AES128-SHA256", "TLS-ECDH-RSA-WITH-AES-128-CBC-SHA256", | ||
| 881 | "ECDH-RSA-AES256-SHA384", "TLS-ECDH-RSA-WITH-AES-256-CBC-SHA384", | ||
| 882 | "ECDHE-ECDSA-AES128-GCM-SHA256", "TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256", | ||
| 883 | "ECDHE-ECDSA-AES256-GCM-SHA384", "TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384", | ||
| 884 | "ECDH-ECDSA-AES128-GCM-SHA256", "TLS-ECDH-ECDSA-WITH-AES-128-GCM-SHA256", | ||
| 885 | "ECDH-ECDSA-AES256-GCM-SHA384", "TLS-ECDH-ECDSA-WITH-AES-256-GCM-SHA384", | ||
| 886 | "ECDHE-RSA-AES128-GCM-SHA256", "TLS-ECDHE-RSA-WITH-AES-128-GCM-SHA256", | ||
| 887 | "ECDHE-RSA-AES256-GCM-SHA384", "TLS-ECDHE-RSA-WITH-AES-256-GCM-SHA384", | ||
| 888 | "ECDH-RSA-AES128-GCM-SHA256", "TLS-ECDH-RSA-WITH-AES-128-GCM-SHA256", | ||
| 889 | "ECDH-RSA-AES256-GCM-SHA384", "TLS-ECDH-RSA-WITH-AES-256-GCM-SHA384", | ||
| 890 | "ECDHE-PSK-RC4-SHA", "TLS-ECDHE-PSK-WITH-RC4-128-SHA", | ||
| 891 | "ECDHE-PSK-3DES-EDE-CBC-SHA", "TLS-ECDHE-PSK-WITH-3DES-EDE-CBC-SHA", | ||
| 892 | "ECDHE-PSK-AES128-CBC-SHA", "TLS-ECDHE-PSK-WITH-AES-128-CBC-SHA", | ||
| 893 | "ECDHE-PSK-AES256-CBC-SHA", "TLS-ECDHE-PSK-WITH-AES-256-CBC-SHA", | ||
| 894 | "ECDHE-PSK-AES128-CBC-SHA256", "TLS-ECDHE-PSK-WITH-AES-128-CBC-SHA256", | ||
| 895 | "ECDHE-PSK-AES256-CBC-SHA384", "TLS-ECDHE-PSK-WITH-AES-256-CBC-SHA384", | ||
| 896 | "ECDHE-PSK-NULL-SHA", "TLS-ECDHE-PSK-WITH-NULL-SHA", | ||
| 897 | "ECDHE-PSK-NULL-SHA256", "TLS-ECDHE-PSK-WITH-NULL-SHA256", | ||
| 898 | "ECDHE-PSK-NULL-SHA384", "TLS-ECDHE-PSK-WITH-NULL-SHA384", | ||
| 899 | "ECDHE-ECDSA-CAMELLIA128-SHA256", "TLS-ECDHE-ECDSA-WITH-CAMELLIA-128-CBC-SHA256", | ||
| 900 | "ECDHE-ECDSA-CAMELLIA256-SHA38", "TLS-ECDHE-ECDSA-WITH-CAMELLIA-256-CBC-SHA384", | ||
| 901 | "ECDH-ECDSA-CAMELLIA128-SHA256", "TLS-ECDH-ECDSA-WITH-CAMELLIA-128-CBC-SHA256", | ||
| 902 | "ECDH-ECDSA-CAMELLIA256-SHA384", "TLS-ECDH-ECDSA-WITH-CAMELLIA-256-CBC-SHA384", | ||
| 903 | "ECDHE-RSA-CAMELLIA128-SHA256", "TLS-ECDHE-RSA-WITH-CAMELLIA-128-CBC-SHA256", | ||
| 904 | "ECDHE-RSA-CAMELLIA256-SHA384", "TLS-ECDHE-RSA-WITH-CAMELLIA-256-CBC-SHA384", | ||
| 905 | "ECDH-RSA-CAMELLIA128-SHA256", "TLS-ECDH-RSA-WITH-CAMELLIA-128-CBC-SHA256", | ||
| 906 | "ECDH-RSA-CAMELLIA256-SHA384", "TLS-ECDH-RSA-WITH-CAMELLIA-256-CBC-SHA384", | ||
| 907 | "PSK-CAMELLIA128-SHA256", "TLS-PSK-WITH-CAMELLIA-128-CBC-SHA256", | ||
| 908 | "PSK-CAMELLIA256-SHA384", "TLS-PSK-WITH-CAMELLIA-256-CBC-SHA384", | ||
| 909 | "DHE-PSK-CAMELLIA128-SHA256", "TLS-DHE-PSK-WITH-CAMELLIA-128-CBC-SHA256", | ||
| 910 | "DHE-PSK-CAMELLIA256-SHA384", "TLS-DHE-PSK-WITH-CAMELLIA-256-CBC-SHA384", | ||
| 911 | "RSA-PSK-CAMELLIA128-SHA256", "TLS-RSA-PSK-WITH-CAMELLIA-128-CBC-SHA256", | ||
| 912 | "RSA-PSK-CAMELLIA256-SHA384", "TLS-RSA-PSK-WITH-CAMELLIA-256-CBC-SHA384", | ||
| 913 | "ECDHE-PSK-CAMELLIA128-SHA256", "TLS-ECDHE-PSK-WITH-CAMELLIA-128-CBC-SHA256", | ||
| 914 | "ECDHE-PSK-CAMELLIA256-SHA384", "TLS-ECDHE-PSK-WITH-CAMELLIA-256-CBC-SHA384", | ||
| 915 | "AES128-CCM", "TLS-RSA-WITH-AES-128-CCM", | ||
| 916 | "AES256-CCM", "TLS-RSA-WITH-AES-256-CCM", | ||
| 917 | "DHE-RSA-AES128-CCM", "TLS-DHE-RSA-WITH-AES-128-CCM", | ||
| 918 | "DHE-RSA-AES256-CCM", "TLS-DHE-RSA-WITH-AES-256-CCM", | ||
| 919 | "AES128-CCM8", "TLS-RSA-WITH-AES-128-CCM-8", | ||
| 920 | "AES256-CCM8", "TLS-RSA-WITH-AES-256-CCM-8", | ||
| 921 | "DHE-RSA-AES128-CCM8", "TLS-DHE-RSA-WITH-AES-128-CCM-8", | ||
| 922 | "DHE-RSA-AES256-CCM8", "TLS-DHE-RSA-WITH-AES-256-CCM-8", | ||
| 923 | "PSK-AES128-CCM", "TLS-PSK-WITH-AES-128-CCM", | ||
| 924 | "PSK-AES256-CCM", "TLS-PSK-WITH-AES-256-CCM", | ||
| 925 | "DHE-PSK-AES128-CCM", "TLS-DHE-PSK-WITH-AES-128-CCM", | ||
| 926 | "DHE-PSK-AES256-CCM", "TLS-DHE-PSK-WITH-AES-256-CCM", | ||
| 927 | "PSK-AES128-CCM8", "TLS-PSK-WITH-AES-128-CCM-8", | ||
| 928 | "PSK-AES256-CCM8", "TLS-PSK-WITH-AES-256-CCM-8", | ||
| 929 | "DHE-PSK-AES128-CCM8", "TLS-PSK-DHE-WITH-AES-128-CCM-8", | ||
| 930 | "DHE-PSK-AES256-CCM8", "TLS-PSK-DHE-WITH-AES-256-CCM-8", | ||
| 931 | "ECDHE-ECDSA-AES128-CCM", "TLS-ECDHE-ECDSA-WITH-AES-128-CCM", | ||
| 932 | "ECDHE-ECDSA-AES256-CCM", "TLS-ECDHE-ECDSA-WITH-AES-256-CCM", | ||
| 933 | "ECDHE-ECDSA-AES128-CCM8", "TLS-ECDHE-ECDSA-WITH-AES-128-CCM-8", | ||
| 934 | "ECDHE-ECDSA-AES256-CCM8", "TLS-ECDHE-ECDSA-WITH-AES-256-CCM-8", | ||
| 935 | "ECDHE-RSA-CHACHA20-POLY1305-OLD", "TLS-ECDHE-RSA-WITH-CHACHA20-POLY1305-SHA256-OLD", | ||
| 936 | "ECDHE-ECDSA-CHACHA20-POLY1305-OLD", "TLS-ECDHE-ECDSA-WITH-CHACHA20-POLY1305-SHA256-OLD", | ||
| 937 | "DHE-RSA-CHACHA20-POLY1305-OLD", "TLS-DHE-RSA-WITH-CHACHA20-POLY1305-SHA256-OLD", | ||
| 938 | "ECDHE-RSA-CHACHA20-POLY1305", "TLS-ECDHE-RSA-WITH-CHACHA20-POLY1305-SHA256", | ||
| 939 | "ECDHE-ECDSA-CHACHA20-POLY1305", "TLS-ECDHE-ECDSA-WITH-CHACHA20-POLY1305-SHA256", | ||
| 940 | "DHE-RSA-CHACHA20-POLY1305", "TLS-DHE-RSA-WITH-CHACHA20-POLY1305-SHA256", | ||
| 941 | "PSK-CHACHA20-POLY1305", "TLS-PSK-WITH-CHACHA20-POLY1305-SHA256", | ||
| 942 | "ECDHE-PSK-CHACHA20-POLY1305", "TLS-ECDHE-PSK-WITH-CHACHA20-POLY1305-SHA256", | ||
| 943 | "DHE-PSK-CHACHA20-POLY1305", "TLS-DHE-PSK-WITH-CHACHA20-POLY1305-SHA256", | ||
| 944 | "RSA-PSK-CHACHA20-POLY1305", "TLS-RSA-PSK-WITH-CHACHA20-POLY1305-SHA256", | ||
| 945 | "GOST-MD5", "TLS-GOSTR341094-RSA-WITH-28147-CNT-MD5", | ||
| 946 | "GOST-GOST94", "TLS-RSA-WITH-28147-CNT-GOST94", | ||
| 947 | "RC4-MD5", "SSL-CK-RC4-128-WITH-MD5", | ||
| 948 | "EXP-RC4-MD5", "SSL-CK-RC4-128-EXPORT40-WITH-MD5", | ||
| 949 | "RC2-CBC-MD5", "SSL-CK-RC2-128-CBC-WITH-MD5", | ||
| 950 | "EXP-RC2-CBC-MD5", "SSL-CK-RC2-128-CBC-EXPORT40-WITH-MD5", | ||
| 951 | "IDEA-CBC-MD5", "SSL-CK-IDEA-128-CBC-WITH-MD5", | ||
| 952 | "DES-CBC-MD5", "SSL-CK-DES-64-CBC-WITH-MD5", | ||
| 953 | "DES-CBC-SHA", "SSL-CK-DES-64-CBC-WITH-SHA", | ||
| 954 | "DES-CBC3-MD5", "SSL-CK-DES-192-EDE3-CBC-WITH-MD5", | ||
| 955 | "DES-CBC3-SHA", "SSL-CK-DES-192-EDE3-CBC-WITH-SHA", | ||
| 956 | "RC4-64-MD5", "SSL-CK-RC4-64-WITH-MD5", | ||
| 957 | "DES-CFB-M1", "SSL-CK-DES-64-CFB64-WITH-MD5-1", | ||
| 958 | NULL | ||
| 959 | }; | ||
| 960 | // fprintf(stderr, "SUCCESS: %s => %s => %d\n\n", xlate_openssl[i], xlate_openssl[i+1], mbedtls_ssl_get_ciphersuite_id(xlate_openssl[i+1])); | ||
| 961 | static int map_openssl_suite(char *openssl_name) { | ||
| 962 | int i; | ||
| 963 | for (i = 0; xlate_openssl[i]; i += 2) { | ||
| 964 | if (!strcmp(xlate_openssl[i], openssl_name)) | ||
| 965 | return mbedtls_ssl_get_ciphersuite_id(xlate_openssl[i + 1]); | ||
| 966 | } | ||
| 967 | return 0; | ||
| 968 | } | ||
| 969 | |||
| 970 | #endif | ||
diff --git a/vchat-tls.h b/vchat-tls.h new file mode 100644 index 0000000..2771173 --- /dev/null +++ b/vchat-tls.h | |||
| @@ -0,0 +1,58 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | /* prototypes */ | ||
| 4 | |||
| 5 | typedef int (*vc_askpass_cb_t)(char *, int, int, void *); | ||
| 6 | struct vc_x509store_t { | ||
| 7 | char *cafile; | ||
| 8 | char *capath; | ||
| 9 | char *crlfile; | ||
| 10 | vc_askpass_cb_t askpass_callback; | ||
| 11 | char *certfile; | ||
| 12 | char *keyfile; | ||
| 13 | int flags; | ||
| 14 | }; | ||
| 15 | typedef struct vc_x509store_t vc_x509store_t; | ||
| 16 | |||
| 17 | void vc_x509store_set_pkeycb(vc_x509store_t *, vc_askpass_cb_t); | ||
| 18 | void vc_x509store_setflags(vc_x509store_t *, int); | ||
| 19 | void vc_x509store_setkeyfile(vc_x509store_t *, char *); | ||
| 20 | void vc_x509store_setcertfile(vc_x509store_t *, char *); | ||
| 21 | void vc_x509store_setcafile(vc_x509store_t *, char *); | ||
| 22 | void vc_x509store_clearflags(vc_x509store_t *, int); | ||
| 23 | void vc_x509store_setcapath(vc_x509store_t *, char *); | ||
| 24 | void vc_x509store_setcrlfile(vc_x509store_t *, char *); | ||
| 25 | void vc_cleanup_x509store(vc_x509store_t *s); | ||
| 26 | |||
| 27 | #if !defined(TLS_LIB_OPENSSL) && !defined(TLS_LIB_MBEDTLS) | ||
| 28 | #error \ | ||
| 29 | "Neither TLS_LIB_OPENSSL nor TLS_LIB_MBEDTLS are defined. Please select at least one." | ||
| 30 | #endif | ||
| 31 | |||
| 32 | #ifdef TLS_LIB_OPENSSL | ||
| 33 | void vc_openssl_init_x509store(vc_x509store_t *); | ||
| 34 | int vc_openssl_connect(int serverfd, vc_x509store_t *); | ||
| 35 | ssize_t vc_openssl_sendmessage(const void *buf, size_t size); | ||
| 36 | ssize_t vc_openssl_receivemessage(void *buf, size_t size); | ||
| 37 | void vc_openssl_cleanup(); | ||
| 38 | char *vc_openssl_version(); | ||
| 39 | #endif | ||
| 40 | |||
| 41 | #ifdef TLS_LIB_MBEDTLS | ||
| 42 | void vc_mbedtls_init_x509store(vc_x509store_t *); | ||
| 43 | int vc_mbedtls_connect(int serverfd, vc_x509store_t *); | ||
| 44 | ssize_t vc_mbedtls_sendmessage(const void *buf, size_t size); | ||
| 45 | ssize_t vc_mbedtls_receivemessage(void *buf, size_t size); | ||
| 46 | void vc_mbedtls_cleanup(); | ||
| 47 | char *vc_mbedtls_version(); | ||
| 48 | #endif | ||
| 49 | |||
| 50 | #define VC_X509S_USE_CAFILE 0x01 | ||
| 51 | #define VC_X509S_USE_CAPATH 0x02 | ||
| 52 | #define VC_X509S_USE_CERTIFICATE 0x04 | ||
| 53 | #define VC_X509S_SSL_VERIFY_NONE 0x10 | ||
| 54 | #define VC_X509S_SSL_VERIFY_PEER 0x20 | ||
| 55 | #define VC_X509S_SSL_VERIFY_FAIL_IF_NO_PEER_CERT 0x40 | ||
| 56 | #define VC_X509S_SSL_VERIFY_CLIENT_ONCE 0x80 | ||
| 57 | #define VC_X509S_SSL_VERIFY_MASK 0xF0 | ||
| 58 | |||
diff --git a/vchat-ui.c b/vchat-ui.c index 82a6840..185cad0 100755..100644 --- a/vchat-ui.c +++ b/vchat-ui.c | |||
| @@ -10,116 +10,124 @@ | |||
| 10 | * without even the implied warranty of merchantability or fitness for a | 10 | * without even the implied warranty of merchantability or fitness for a |
| 11 | * particular purpose. In no event shall the copyright holder be liable for | 11 | * particular purpose. In no event shall the copyright holder be liable for |
| 12 | * any direct, indirect, incidental or special damages arising in any way out | 12 | * any direct, indirect, incidental or special damages arising in any way out |
| 13 | * of the use of this software. | 13 | * of the use of this software. |
| 14 | * | 14 | * |
| 15 | */ | 15 | */ |
| 16 | 16 | ||
| 17 | /* general includes */ | 17 | /* general includes */ |
| 18 | #include <unistd.h> | 18 | #include <errno.h> |
| 19 | #include <stdint.h> | ||
| 20 | #include <ncurses.h> | 19 | #include <ncurses.h> |
| 20 | #include <readline/history.h> | ||
| 21 | #include <readline/readline.h> | ||
| 22 | #include <regex.h> | ||
| 21 | #include <signal.h> | 23 | #include <signal.h> |
| 24 | #include <stdint.h> | ||
| 22 | #include <stdlib.h> | 25 | #include <stdlib.h> |
| 26 | #include <string.h> | ||
| 23 | #include <strings.h> | 27 | #include <strings.h> |
| 24 | #include <errno.h> | ||
| 25 | #include <termios.h> | ||
| 26 | #include <sys/ioctl.h> | 28 | #include <sys/ioctl.h> |
| 29 | #include <termios.h> | ||
| 27 | #include <time.h> | 30 | #include <time.h> |
| 28 | #include <string.h> | 31 | #include <unistd.h> |
| 29 | #include <readline/readline.h> | ||
| 30 | #include <readline/history.h> | ||
| 31 | #include <regex.h> | ||
| 32 | #include <wchar.h> | 32 | #include <wchar.h> |
| 33 | 33 | ||
| 34 | #include "vchat.h" | ||
| 35 | #include "vchat-user.h" | 34 | #include "vchat-user.h" |
| 35 | #include "vchat.h" | ||
| 36 | 36 | ||
| 37 | /* version of this module */ | 37 | /* version of this module */ |
| 38 | const char *vchat_ui_version = "vchat-ui.c $Id$"; | 38 | const char *vchat_ui_version = |
| 39 | "vchat-ui.c $Id$"; | ||
| 39 | 40 | ||
| 40 | /* externally used variables */ | 41 | /* externally used variables */ |
| 41 | /* current string in topic window */ | 42 | /* current string in topic window */ |
| 42 | char topicstr[TOPICSTRSIZE] = "[] VChat 0.19"; | 43 | char topicstr[TOPICSTRSIZE] = "[] VChat 0.20"; |
| 43 | /* current string in console window */ | 44 | /* current string in console window */ |
| 44 | char consolestr[CONSOLESTRSIZE] = "[ Get help: .h for server /h for client commands"; | 45 | char consolestr[CONSOLESTRSIZE] = |
| 46 | "[ Get help: .h for server /h for client commands"; | ||
| 45 | 47 | ||
| 46 | static unsigned int ui_init = 0; | 48 | static unsigned int ui_init = 0; |
| 47 | 49 | ||
| 48 | /* our windows */ | 50 | /* our windows */ |
| 49 | static WINDOW *console = NULL; | 51 | static WINDOW *console = NULL; |
| 50 | static WINDOW *input = NULL; | 52 | static WINDOW *input = NULL; |
| 51 | static WINDOW *topic = NULL; | 53 | static WINDOW *topic = NULL; |
| 52 | static WINDOW *channel = NULL; | 54 | static WINDOW *channel = NULL; |
| 53 | static WINDOW *private = NULL; | 55 | static WINDOW *private = NULL; |
| 54 | static WINDOW *output = NULL; | 56 | static WINDOW *output = NULL; |
| 55 | 57 | ||
| 56 | /* our screen dimensions */ | 58 | /* our screen dimensions */ |
| 57 | static int screensx = 0; | 59 | static int screensx = 0; |
| 58 | static int screensy = 0; | 60 | static int screensy = 0; |
| 59 | /* current horizontal scrolling offset for input line */ | 61 | /* current horizontal scrolling offset for input line */ |
| 60 | static int scroff = 0; | 62 | static int scroff = 0; |
| 61 | /* cache for stepping value of horizontal scrolling */ | 63 | /* cache for stepping value of horizontal scrolling */ |
| 62 | unsigned int hscroll = 0; | 64 | unsigned int hscroll = 0; |
| 63 | 65 | ||
| 64 | static int outputshown = 0; | 66 | static int outputshown = 0; |
| 65 | static int outputwidth_desired = 0; | 67 | static int outputwidth_desired = 0; |
| 66 | 68 | ||
| 67 | static int privheight = 0; | 69 | static int privheight = 0; |
| 68 | static int privheight_desired = 0; | 70 | static int privheight_desired = 0; |
| 69 | static int privwinhidden = 0; | 71 | static int privwinhidden = 0; |
| 70 | int usetime = 1; | 72 | int usetime = 1; |
| 71 | int outputcountdown = 0; | 73 | int outputcountdown = 0; |
| 72 | char *querypartner = NULL; | 74 | char *querypartner = NULL; |
| 73 | 75 | ||
| 74 | struct sb_entry { | 76 | struct sb_entry { |
| 75 | int id; | 77 | int id; |
| 76 | time_t when; | 78 | time_t when; |
| 77 | int stamp; | 79 | int stamp; |
| 78 | char *what; | 80 | char *what; |
| 79 | struct sb_entry *link; | 81 | struct sb_entry *link; |
| 80 | }; | 82 | }; |
| 81 | 83 | ||
| 82 | struct sb_data { | 84 | struct sb_data { |
| 83 | struct sb_entry *entries; | 85 | struct sb_entry *entries; |
| 84 | struct sb_entry *last; | 86 | struct sb_entry *last; |
| 85 | int count; | 87 | int count; |
| 86 | int scroll; | 88 | int scroll; |
| 87 | }; | 89 | }; |
| 88 | 90 | ||
| 89 | static struct sb_data *sb_pub = NULL; | 91 | static struct sb_data *sb_pub = NULL; |
| 90 | static struct sb_data *sb_priv = NULL; | 92 | static struct sb_data *sb_priv = NULL; |
| 91 | static struct sb_data *sb_out = NULL; | 93 | static struct sb_data *sb_out = NULL; |
| 94 | static struct sb_data *sb_connect = NULL; | ||
| 92 | 95 | ||
| 93 | /* Tells, which window is active */ | 96 | /* Tells, which window is active */ |
| 94 | static int sb_win = 0; /* 0 for pub, 1 for priv */ | 97 | static int sb_win = 0; /* 0 for pub, 1 for priv */ |
| 95 | 98 | ||
| 96 | /* struct to keep filter list */ | 99 | /* struct to keep filter list */ |
| 97 | struct filt { | 100 | struct filt { |
| 98 | char colour; | 101 | char colour; |
| 99 | unsigned int id; | 102 | unsigned int id; |
| 100 | char *text; | 103 | char *text; |
| 101 | regex_t regex; | 104 | regex_t regex; |
| 102 | struct filt *next; | 105 | struct filt *next; |
| 103 | }; | 106 | }; |
| 104 | 107 | ||
| 105 | typedef struct filt filt; | 108 | typedef struct filt filt; |
| 106 | 109 | ||
| 107 | static filt *filterlist = NULL; | 110 | static filt *filterlist = NULL; |
| 108 | static int filtertype = 0; | 111 | static int filtertype = 0; |
| 109 | static int currentstamp = 0; | 112 | static int currentstamp = 0; |
| 110 | 113 | ||
| 111 | /* Prototype declarations */ | 114 | /* Prototype declarations */ |
| 112 | 115 | ||
| 113 | static void resize (int); | 116 | static void resize(int); |
| 114 | static void forceredraw (void); | 117 | static void forceredraw(void); |
| 115 | static void forceredraw_wrapper (int a) {forceredraw();} | 118 | static void forceredraw_wrapper(int a) { |
| 116 | static void drawwin (WINDOW *win, struct sb_data *sb); | 119 | (void)a; |
| 117 | static int writescr (WINDOW *win, struct sb_entry *entry); | 120 | forceredraw(); |
| 118 | static int testfilter ( struct sb_entry *entry); | 121 | } |
| 119 | static int gettextwidth (const char *textbuffer); | 122 | static void drawwin(WINDOW *win, struct sb_data *sb); |
| 120 | static void resize_output (void); | 123 | static int writescr(WINDOW *win, struct sb_entry *entry); |
| 121 | static int getsbeheight (struct sb_entry *entry, const int xwidth, int needstime ); | 124 | static int testfilter(struct sb_entry *entry); |
| 122 | static int getsbdataheight (struct sb_data *data, const int xwidth, int needstime ); | 125 | static int gettextwidth(const char *textbuffer); |
| 126 | static void resize_output(void); | ||
| 127 | static int getsbeheight(struct sb_entry *entry, const int xwidth, | ||
| 128 | int needstime); | ||
| 129 | static int getsbdataheight(struct sb_data *data, const int xwidth, | ||
| 130 | int needstime); | ||
| 123 | /* CURRENTLY UNUSED | 131 | /* CURRENTLY UNUSED |
| 124 | static void writecolorized (WINDOW *win, char *string); | 132 | static void writecolorized (WINDOW *win, char *string); |
| 125 | */ | 133 | */ |
| @@ -132,22 +140,29 @@ enum { | |||
| 132 | }; | 140 | }; |
| 133 | 141 | ||
| 134 | /* */ | 142 | /* */ |
| 135 | static void | 143 | static void togglequery() { |
| 136 | togglequery() { | 144 | if (querypartner && private) { |
| 137 | if( querypartner && private ) { | 145 | { |
| 138 | { struct sb_data *tmp = sb_pub; sb_pub = sb_priv; sb_priv = tmp; } | 146 | struct sb_data *tmp = sb_pub; |
| 139 | { WINDOW *tmp= private; private = channel; channel = tmp; } | 147 | sb_pub = sb_priv; |
| 148 | sb_priv = tmp; | ||
| 149 | } | ||
| 150 | { | ||
| 151 | WINDOW *tmp = private; | ||
| 152 | private | ||
| 153 | = channel; | ||
| 154 | channel = tmp; | ||
| 155 | } | ||
| 140 | } | 156 | } |
| 141 | } | 157 | } |
| 142 | 158 | ||
| 143 | const char * skip_to_character( const char * string, size_t offset ) { | 159 | const char *skip_to_character(const char *string, size_t offset) { |
| 144 | size_t ch_size; | 160 | size_t ch_size; |
| 145 | mbstate_t mbs; | 161 | mbstate_t mbs; |
| 146 | memset(&mbs, 0, sizeof(mbs)); | 162 | memset(&mbs, 0, sizeof(mbs)); |
| 147 | 163 | ||
| 148 | while( offset-- > 0 ) { | 164 | while (offset-- > 0) { |
| 149 | switch( ch_size = mbrlen( string, MB_CUR_MAX, &mbs ) ) | 165 | switch (ch_size = mbrlen(string, MB_CUR_MAX, &mbs)) { |
| 150 | { | ||
| 151 | case (size_t)-1: | 166 | case (size_t)-1: |
| 152 | case (size_t)-2: | 167 | case (size_t)-2: |
| 153 | return NULL; | 168 | return NULL; |
| @@ -161,15 +176,14 @@ const char * skip_to_character( const char * string, size_t offset ) { | |||
| 161 | return string; | 176 | return string; |
| 162 | } | 177 | } |
| 163 | 178 | ||
| 164 | size_t offset_to_character( const char * string, size_t offset ) { | 179 | size_t offset_to_character(const char *string, size_t offset) { |
| 165 | mbstate_t mbs; | 180 | mbstate_t mbs; |
| 166 | memset(&mbs, 0, sizeof(mbs)); | 181 | memset(&mbs, 0, sizeof(mbs)); |
| 167 | const char * string_offset = string + offset; | 182 | const char *string_offset = string + offset; |
| 168 | size_t ch_size, nchars = 0; | 183 | size_t ch_size, nchars = 0; |
| 169 | 184 | ||
| 170 | while( string < string_offset ) { | 185 | while (string < string_offset) { |
| 171 | switch( ch_size = mbrlen( string, MB_CUR_MAX, &mbs ) ) | 186 | switch (ch_size = mbrlen(string, MB_CUR_MAX, &mbs)) { |
| 172 | { | ||
| 173 | case (size_t)-1: | 187 | case (size_t)-1: |
| 174 | case (size_t)-2: | 188 | case (size_t)-2: |
| 175 | return -1; | 189 | return -1; |
| @@ -184,191 +198,195 @@ size_t offset_to_character( const char * string, size_t offset ) { | |||
| 184 | } | 198 | } |
| 185 | 199 | ||
| 186 | /* readlines callback when a line is completed */ | 200 | /* readlines callback when a line is completed */ |
| 187 | static void | 201 | static void linecomplete(char *line) { |
| 188 | linecomplete (char *line) | ||
| 189 | { | ||
| 190 | char *c; | 202 | char *c; |
| 191 | int i; | 203 | int i; |
| 192 | 204 | ||
| 193 | /* send linefeed, return pointer, reset cursors */ | 205 | /* send linefeed, return pointer, reset cursors */ |
| 194 | waddch (input, '\n'); | 206 | waddch(input, '\n'); |
| 195 | wmove (input, 0, 0); | 207 | wmove(input, 0, 0); |
| 196 | scroff = 0; | 208 | scroff = 0; |
| 197 | 209 | ||
| 198 | if (line) { | 210 | if (line) { |
| 199 | i = strlen(line) - 1; | 211 | i = strlen(line) - 1; |
| 200 | while (line[i] == ' ') line[i--]='\0'; | 212 | while (line[i] == ' ') |
| 213 | line[i--] = '\0'; | ||
| 201 | 214 | ||
| 202 | if (line[0] && strchr(line,' ') == NULL && line[i] == ':') | 215 | if (line[0] && strchr(line, ' ') == NULL && line[i] == ':') |
| 203 | line[i--] = '\0'; | 216 | line[i--] = '\0'; |
| 204 | 217 | ||
| 205 | /* empty line? nada. */ | 218 | /* empty line? nada. */ |
| 206 | if (line[0]) { | 219 | if (line[0]) { |
| 207 | /* add line to history and have it handled in vchat-protocol.c */ | 220 | /* add line to history and have it handled in vchat-protocol.c */ |
| 208 | add_history (line); | 221 | add_history(line); |
| 209 | handleline (line); | 222 | handleline(line); |
| 210 | } | 223 | } |
| 211 | free (line); | 224 | free(line); |
| 212 | rl_reset_line_state(); | 225 | rl_reset_line_state(); |
| 213 | rl_point = rl_end = rl_done = 0; | 226 | rl_point = rl_end = rl_done = 0; |
| 214 | 227 | ||
| 215 | /* If in query mode, feed query prefix */ | 228 | /* If in query mode, feed query prefix */ |
| 216 | if (( c = querypartner )) | 229 | if ((c = querypartner)) |
| 217 | while( *c ) rl_stuff_char( *c++ ); | 230 | while (*c) |
| 231 | rl_stuff_char(*c++); | ||
| 218 | 232 | ||
| 219 | /* wipe input line and reset cursor */ | 233 | /* wipe input line and reset cursor */ |
| 220 | wrefresh (input); | 234 | wrefresh(input); |
| 221 | } | 235 | } |
| 222 | } | 236 | } |
| 223 | 237 | ||
| 224 | /* redraw-callback for readline */ | 238 | /* redraw-callback for readline */ |
| 225 | static void | 239 | static void vciredraw(void) { |
| 226 | vciredraw (void) | ||
| 227 | { | ||
| 228 | int i; | 240 | int i; |
| 229 | size_t readline_point; | 241 | size_t readline_point; |
| 230 | 242 | ||
| 231 | /* readline offers us information we don't need | 243 | /* readline offers us information we don't need |
| 232 | so ignore outabound cursor positions */ | 244 | so ignore outabound cursor positions */ |
| 233 | if( rl_point < 0 ) rl_point = 0; | 245 | if (rl_point < 0) |
| 234 | //if( rl_point > rl_end ) rl_point = rl_end; | 246 | rl_point = 0; |
| 247 | // if( rl_point > rl_end ) rl_point = rl_end; | ||
| 235 | 248 | ||
| 236 | readline_point = offset_to_character( rl_line_buffer, rl_point ); | 249 | readline_point = offset_to_character(rl_line_buffer, rl_point); |
| 237 | 250 | ||
| 238 | /* hscroll value cache set up? */ | 251 | /* hscroll value cache set up? */ |
| 239 | if (!hscroll) { | 252 | if (!hscroll) { |
| 240 | /* check config-option or set hardcoded default */ | 253 | /* check config-option or set hardcoded default */ |
| 241 | hscroll = getintoption (CF_HSCROLL); | 254 | hscroll = getintoption(CF_HSCROLL); |
| 242 | if (!hscroll) | 255 | if (!hscroll) |
| 243 | hscroll = 15; | 256 | hscroll = 15; |
| 244 | } | 257 | } |
| 245 | 258 | ||
| 246 | /* calculate horizontal scrolling offset */ | 259 | /* calculate horizontal scrolling offset */ |
| 247 | 260 | ||
| 248 | /* Case 1: readline is left of current scroll offset: Adjust to left to reveal more text */ | 261 | /* Case 1: readline is left of current scroll offset: Adjust to left to reveal |
| 249 | if( readline_point < scroff ) | 262 | * more text */ |
| 263 | if (readline_point < scroff) | ||
| 250 | scroff = readline_point - hscroll; | 264 | scroff = readline_point - hscroll; |
| 251 | if( scroff < 1 ) | 265 | if (scroff < 1) |
| 252 | scroff = 0; | 266 | scroff = 0; |
| 253 | 267 | ||
| 254 | /* Case 2: readline just hit the last char on the line: Adjust to right to leave more space on screen */ | 268 | /* Case 2: readline just hit the last char on the line: Adjust to right to |
| 255 | if( readline_point >= scroff + getmaxx(input) - 1 ) | 269 | * leave more space on screen */ |
| 270 | if (readline_point >= scroff + getmaxx(input) - 1) | ||
| 256 | scroff = readline_point - getmaxx(input) + hscroll; | 271 | scroff = readline_point - getmaxx(input) + hscroll; |
| 257 | 272 | ||
| 258 | /* wipe input line */ | 273 | /* wipe input line */ |
| 259 | wmove (input, 0, 0); | 274 | wmove(input, 0, 0); |
| 260 | for (i = 0; i < getmaxx(input) - 1; i++) | 275 | for (i = 0; i < getmaxx(input) - 1; i++) |
| 261 | waddch (input, ' '); | 276 | waddch(input, ' '); |
| 262 | 277 | ||
| 263 | /* show current line, move cursor, redraw! */ | 278 | /* show current line, move cursor, redraw! */ |
| 264 | const char *start_line = skip_to_character( rl_line_buffer, scroff ); | 279 | const char *start_line = skip_to_character(rl_line_buffer, scroff); |
| 265 | const char *end_line = skip_to_character( start_line, getmaxx(input) - 1 ); | 280 | const char *end_line = skip_to_character(start_line, getmaxx(input) - 1); |
| 266 | |||
| 267 | mvwaddnstr (input, 0, 0, start_line, end_line - start_line ); | ||
| 268 | wmove (input, 0, readline_point - scroff ); | ||
| 269 | wrefresh (input); | ||
| 270 | 281 | ||
| 282 | mvwaddnstr(input, 0, 0, start_line, end_line - start_line); | ||
| 283 | wmove(input, 0, readline_point - scroff); | ||
| 284 | wrefresh(input); | ||
| 271 | } | 285 | } |
| 272 | 286 | ||
| 273 | /* called by the eventloop in vchat-client.c */ | 287 | /* called by the eventloop in vchat-client.c */ |
| 274 | void | 288 | void userinput(void) { |
| 275 | userinput (void) | ||
| 276 | { | ||
| 277 | /* let readline handle what the user typed .. */ | 289 | /* let readline handle what the user typed .. */ |
| 278 | rl_callback_read_char (); | 290 | rl_callback_read_char(); |
| 279 | } | 291 | } |
| 280 | 292 | ||
| 281 | static int | 293 | static int calcdrawcus(char *const str) { |
| 282 | calcdrawcus (char * const str) { | ||
| 283 | char *tmp = str; | 294 | char *tmp = str; |
| 284 | int zero = 0; | 295 | int zero = 0; |
| 285 | while( *tmp && (*tmp!=' ') && (*tmp!='\n')) { if(*tmp==1) zero+=2; tmp++; } | 296 | while (*tmp && (*tmp != ' ') && (*tmp != '\n')) { |
| 297 | if (*tmp == 1) | ||
| 298 | zero += 2; | ||
| 299 | tmp++; | ||
| 300 | } | ||
| 286 | return (tmp - str) - zero; | 301 | return (tmp - str) - zero; |
| 287 | } | 302 | } |
| 288 | 303 | ||
| 289 | static void | 304 | static void sb_flush(struct sb_data *sb) { |
| 290 | sb_flush ( struct sb_data *sb ) { | ||
| 291 | struct sb_entry *now = sb->entries, *prev = NULL, *tmp; | 305 | struct sb_entry *now = sb->entries, *prev = NULL, *tmp; |
| 292 | while( now ) { | 306 | while (now) { |
| 293 | tmp = (struct sb_entry*)((unsigned long)prev ^ (unsigned long)now->link); | 307 | tmp = (struct sb_entry *)((unsigned long)prev ^ (unsigned long)now->link); |
| 294 | free(now->what ); | 308 | free(now->what); |
| 295 | free(now); | 309 | free(now); |
| 296 | prev = now; | 310 | prev = now; |
| 297 | now = tmp; | 311 | now = tmp; |
| 298 | } | 312 | } |
| 299 | sb->entries = NULL; | 313 | sb->entries = NULL; |
| 314 | sb->last = NULL; | ||
| 315 | sb->count = 0; | ||
| 316 | sb->scroll = 0; | ||
| 300 | } | 317 | } |
| 301 | 318 | ||
| 302 | /*static void | 319 | /* |
| 303 | sb_clear ( struct sb_data **sb ) { | 320 | static void sb_clear ( struct sb_data **sb ) { |
| 304 | sb_flush(*sb); | 321 | sb_flush(*sb); |
| 305 | free( *sb ); | 322 | free( *sb ); |
| 306 | *sb = NULL; | 323 | *sb = NULL; |
| 307 | }*/ | 324 | } |
| 308 | 325 | */ | |
| 309 | static struct sb_entry* | 326 | |
| 310 | sb_add (struct sb_data *sb, const char *line, time_t when) { | 327 | static struct sb_entry *sb_add(struct sb_data *sb, const char *line, |
| 311 | struct sb_entry *newone = malloc (sizeof(struct sb_entry)); | 328 | time_t when) { |
| 312 | if( newone ) { | 329 | struct sb_entry *newone = malloc(sizeof(struct sb_entry)); |
| 313 | if( sb->count == sb->scroll ) sb->scroll++; | 330 | if (newone) { |
| 314 | newone->id = sb->count++; | 331 | if (sb->count == sb->scroll) |
| 315 | newone->when = when; | 332 | sb->scroll++; |
| 316 | newone->what = strdup(line); | 333 | newone->id = sb->count++; |
| 317 | newone->link = sb->entries; | 334 | newone->when = when; |
| 318 | newone->stamp= 0xffff; | 335 | newone->what = strdup(line); |
| 319 | if( sb->entries ) | 336 | newone->link = sb->entries; |
| 320 | sb->entries->link = (struct sb_entry*)((unsigned long)sb->entries->link ^ (unsigned long)newone); | 337 | newone->stamp = 0xffff; |
| 321 | else | 338 | if (sb->entries) |
| 322 | sb->last = newone; | 339 | sb->entries->link = (struct sb_entry *)((unsigned long)sb->entries->link ^ |
| 323 | sb->entries = newone; | 340 | (unsigned long)newone); |
| 341 | else | ||
| 342 | sb->last = newone; | ||
| 343 | sb->entries = newone; | ||
| 324 | } | 344 | } |
| 325 | return newone; | 345 | return newone; |
| 326 | } | 346 | } |
| 327 | 347 | ||
| 328 | void flushout ( ) | 348 | void flushout() { |
| 329 | { | ||
| 330 | sb_flush(sb_out); | 349 | sb_flush(sb_out); |
| 331 | writeout(" "); | 350 | writeout(" "); |
| 332 | outputwidth_desired = 0; | 351 | outputwidth_desired = 0; |
| 333 | outputshown = 0; | 352 | outputshown = 0; |
| 334 | } | 353 | } |
| 335 | 354 | ||
| 336 | void hideout( ) | 355 | void hideout() { |
| 337 | { | 356 | if (outputshown) { |
| 338 | if( outputshown ) { | 357 | outputshown = 0; |
| 339 | outputshown = 0; | 358 | resize(0); |
| 340 | resize(0); | ||
| 341 | } | 359 | } |
| 342 | } | 360 | } |
| 343 | 361 | ||
| 344 | void showout (void) | 362 | void showout(void) { |
| 345 | { | ||
| 346 | writeout(" "); | 363 | writeout(" "); |
| 347 | outputcountdown = 6; | 364 | outputcountdown = 6; |
| 348 | outputshown = 1; | 365 | outputshown = 1; |
| 349 | resize(0); | 366 | resize(0); |
| 350 | } | 367 | } |
| 351 | 368 | ||
| 352 | void writeout (const char *str) | 369 | void writeout(const char *str) { |
| 353 | { | ||
| 354 | int i; | 370 | int i; |
| 355 | sb_add(sb_out,str,time(NULL)); | 371 | sb_add(sb_out, str, time(NULL)); |
| 356 | i = 1 + gettextwidth( str ); | 372 | i = 1 + gettextwidth(str); |
| 357 | if( i > outputwidth_desired ) outputwidth_desired = i; | 373 | if (i > outputwidth_desired) |
| 374 | outputwidth_desired = i; | ||
| 358 | } | 375 | } |
| 359 | 376 | ||
| 360 | int writechan (char *str) { | 377 | int writechan(char *str) { |
| 361 | struct sb_entry *tmp; | 378 | struct sb_entry *tmp; |
| 362 | int i = 0; | 379 | int i = 0; |
| 363 | time_t now = time(NULL); | 380 | time_t now = time(NULL); |
| 364 | tmp = sb_add(sb_pub,str,now); | 381 | tmp = sb_add(sb_pub, str, now); |
| 365 | 382 | ||
| 366 | if ( (sb_pub->scroll == sb_pub->count) && ((filtertype == 0) || ( testfilter(tmp)))) { | 383 | if ((sb_pub->scroll == sb_pub->count) && |
| 367 | i = writescr(channel, tmp); | 384 | ((filtertype == 0) || (testfilter(tmp)))) { |
| 368 | wnoutrefresh(channel); | 385 | i = writescr(channel, tmp); |
| 386 | wnoutrefresh(channel); | ||
| 369 | } | 387 | } |
| 370 | 388 | ||
| 371 | if( querypartner && private ) | 389 | if (querypartner && private) |
| 372 | topicline(NULL); | 390 | topicline(NULL); |
| 373 | else | 391 | else |
| 374 | consoleline(NULL); | 392 | consoleline(NULL); |
| @@ -376,55 +394,73 @@ int writechan (char *str) { | |||
| 376 | return i; | 394 | return i; |
| 377 | } | 395 | } |
| 378 | 396 | ||
| 379 | int writecf (formtstr id, char *str) { | 397 | int writecf(formtstr id, char *str) { |
| 380 | struct sb_entry *tmp; | 398 | struct sb_entry *tmp; |
| 381 | int i = 0; | 399 | int i = 0; |
| 382 | time_t now = time(NULL); | 400 | time_t now = time(NULL); |
| 383 | snprintf(tmpstr,TMPSTRSIZE,getformatstr(id),str); | 401 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(id), str); |
| 384 | tmp = sb_add(sb_pub,tmpstr,now); | 402 | tmp = sb_add(sb_pub, tmpstr, now); |
| 385 | 403 | ||
| 386 | if ( (sb_pub->scroll == sb_pub->count) && | 404 | if ((sb_pub->scroll == sb_pub->count) && |
| 387 | ((filtertype == 0) || ( testfilter(tmp)))) { | 405 | ((filtertype == 0) || (testfilter(tmp)))) { |
| 388 | i = writescr(channel, tmp); | 406 | i = writescr(channel, tmp); |
| 389 | wnoutrefresh(channel); | 407 | wnoutrefresh(channel); |
| 390 | } | 408 | } |
| 391 | 409 | ||
| 392 | if( querypartner && private ) | 410 | if (querypartner && private) |
| 393 | topicline(NULL); | 411 | topicline(NULL); |
| 394 | else | 412 | else |
| 395 | consoleline(NULL); | 413 | consoleline(NULL); |
| 396 | 414 | ||
| 415 | if (!loggedin) | ||
| 416 | sb_add(sb_connect, str, now); | ||
| 417 | |||
| 397 | return i; | 418 | return i; |
| 398 | } | 419 | } |
| 399 | 420 | ||
| 400 | int writepriv (char *str, int maybeep) { | 421 | void dumpconnect() { |
| 422 | struct sb_entry *now = sb_connect->entries, *prev = NULL, *tmp; | ||
| 423 | while (now) { | ||
| 424 | tmp = (struct sb_entry *)((unsigned long)prev ^ (unsigned long)now->link); | ||
| 425 | fputs(now->what, stderr); | ||
| 426 | fputc(10, stderr); | ||
| 427 | prev = now; | ||
| 428 | now = tmp; | ||
| 429 | } | ||
| 430 | } | ||
| 431 | |||
| 432 | void flushconnect() { | ||
| 433 | sb_flush(sb_connect); | ||
| 434 | } | ||
| 435 | |||
| 436 | int writepriv(char *str, int maybeep) { | ||
| 401 | int i = 0; | 437 | int i = 0; |
| 402 | if (private) { | 438 | if (private) { |
| 403 | 439 | ||
| 404 | time_t now = time (NULL); | 440 | time_t now = time(NULL); |
| 405 | struct sb_entry *tmp; | 441 | struct sb_entry *tmp; |
| 406 | tmp = sb_add(sb_priv,str,now); | 442 | tmp = sb_add(sb_priv, str, now); |
| 407 | 443 | ||
| 408 | if ( !privwinhidden && (sb_priv->scroll == sb_priv->count) && | 444 | if (!privwinhidden && (sb_priv->scroll == sb_priv->count) && |
| 409 | ((filtertype == 0) || ( testfilter(tmp)))) { | 445 | ((filtertype == 0) || (testfilter(tmp)))) { |
| 410 | i = writescr(private, tmp); | 446 | i = writescr(private, tmp); |
| 411 | } | 447 | } |
| 412 | if( privwinhidden && !querypartner ) { | 448 | if (privwinhidden && !querypartner) { |
| 413 | if( (maybeep != 0) && (getintoption( CF_BELLPRIV ) != 0 )) | 449 | if ((maybeep != 0) && (getintoption(CF_BELLPRIV) != 0)) |
| 414 | putchar( 7 ); | 450 | putchar(7); |
| 415 | privheight_desired = privwinhidden; | 451 | privheight_desired = privwinhidden; |
| 416 | privwinhidden = 0; | 452 | privwinhidden = 0; |
| 417 | resize(0); | 453 | resize(0); |
| 418 | } | 454 | } |
| 419 | wnoutrefresh(private); | 455 | wnoutrefresh(private); |
| 420 | 456 | ||
| 421 | if( querypartner && private ) | 457 | if (querypartner && private) |
| 422 | consoleline(NULL); | 458 | consoleline(NULL); |
| 423 | else | 459 | else |
| 424 | topicline(NULL); | 460 | topicline(NULL); |
| 425 | 461 | ||
| 426 | } else | 462 | } else |
| 427 | i = writechan( str ); | 463 | i = writechan(str); |
| 428 | 464 | ||
| 429 | return i; | 465 | return i; |
| 430 | } | 466 | } |
| @@ -434,338 +470,364 @@ int writepriv (char *str, int maybeep) { | |||
| 434 | #if NCURSES_VERSION_MAJOR >= 5 | 470 | #if NCURSES_VERSION_MAJOR >= 5 |
| 435 | 471 | ||
| 436 | typedef struct { | 472 | typedef struct { |
| 437 | attr_t attr; | 473 | attr_t attr; |
| 438 | short pair; | 474 | short pair; |
| 439 | } ncurs_attr; | 475 | } ncurs_attr; |
| 440 | 476 | ||
| 441 | #define WATTR_GET( win, orgattr ) wattr_get( win, &orgattr.attr, &orgattr.pair, NULL) | 477 | #define WATTR_GET(win, orgattr) \ |
| 442 | #define WATTR_SET( win, orgattr ) wattr_set( win, orgattr.attr, orgattr.pair, NULL) | 478 | wattr_get(win, &orgattr.attr, &orgattr.pair, NULL) |
| 443 | #define BCOLR_SET( attr, colour ) attr->pair = colour; | 479 | #define WATTR_SET(win, orgattr) wattr_set(win, orgattr.attr, orgattr.pair, NULL) |
| 480 | #define BCOLR_SET(attr, colour) attr->pair = colour; | ||
| 444 | 481 | ||
| 445 | #else | 482 | #else |
| 446 | 483 | ||
| 447 | typedef struct { | 484 | typedef struct { |
| 448 | attr_t attr; | 485 | attr_t attr; |
| 449 | } ncurs_attr; | 486 | } ncurs_attr; |
| 450 | 487 | ||
| 451 | #define WATTR_GET( win, orgattr ) orgattr.attr = wattr_get(win); | 488 | #define WATTR_GET(win, orgattr) orgattr.attr = wattr_get(win); |
| 452 | #define WATTR_SET( win, orgattr ) wattr_set( win, orgattr.attr); | 489 | #define WATTR_SET(win, orgattr) wattr_set(win, orgattr.attr); |
| 453 | #define BCOLR_SET( attr, colour ) attr->attr = ((attr->attr) & ~A_COLOR) | COLOR_PAIR((colour)); | 490 | #define BCOLR_SET(attr, colour) \ |
| 491 | attr->attr = ((attr->attr) & ~A_COLOR) | COLOR_PAIR((colour)); | ||
| 454 | 492 | ||
| 455 | #endif | 493 | #endif |
| 456 | 494 | ||
| 457 | /* 'A' - 'Z' attriute type */ | 495 | /* 'A' - 'Z' attriute type */ |
| 458 | static int attributes[] = { A_ALTCHARSET, A_BOLD, 0, A_DIM, 0, 0, 0, 0, A_INVIS, 0, 0, A_BLINK, | 496 | static int attributes[] = {A_ALTCHARSET, |
| 459 | 0, A_NORMAL, 0, A_PROTECT, 0, A_REVERSE, A_STANDOUT, 0, A_UNDERLINE, | 497 | A_BOLD, |
| 460 | 0, 0, 1, 0, 0 }; | 498 | 0, |
| 461 | 499 | A_DIM, | |
| 462 | static void | 500 | 0, |
| 463 | docolorize (char colour, ncurs_attr *attr, ncurs_attr orgattr) { | 501 | 0, |
| 464 | if( colour== '0') { | 502 | 0, |
| 465 | *attr = orgattr; | 503 | 0, |
| 466 | } else if( ( colour > '0') && ( colour <= '9')) { | 504 | A_INVIS, |
| 467 | BCOLR_SET( attr, colour - '0' ); | 505 | 0, |
| 506 | 0, | ||
| 507 | A_BLINK, | ||
| 508 | 0, | ||
| 509 | A_NORMAL, | ||
| 510 | 0, | ||
| 511 | A_PROTECT, | ||
| 512 | 0, | ||
| 513 | A_REVERSE, | ||
| 514 | A_STANDOUT, | ||
| 515 | 0, | ||
| 516 | A_UNDERLINE, | ||
| 517 | 0, | ||
| 518 | 0, | ||
| 519 | 1, | ||
| 520 | 0, | ||
| 521 | 0}; | ||
| 522 | |||
| 523 | static void docolorize(char colour, ncurs_attr *attr, ncurs_attr orgattr) { | ||
| 524 | if (colour == '0') { | ||
| 525 | *attr = orgattr; | ||
| 526 | } else if ((colour > '0') && (colour <= '9')) { | ||
| 527 | BCOLR_SET(attr, colour - '0'); | ||
| 468 | } else { | 528 | } else { |
| 469 | char upc = colour & ( 0x20 ^ 0xff ); /* colour AND NOT 0x20 */ | 529 | char upc = colour & (0x20 ^ 0xff); /* colour AND NOT 0x20 */ |
| 470 | attr_t newattr; | 530 | attr_t newattr; |
| 471 | if( ( upc >= 'A') && ( upc<='Z' ) && ( newattr = attributes[upc - 'A']) ) | 531 | if ((upc >= 'A') && (upc <= 'Z') && (newattr = attributes[upc - 'A'])) |
| 472 | attr->attr = ( colour & 0x20 ) ? attr->attr | newattr : attr->attr & ~newattr; | 532 | attr->attr = |
| 533 | (colour & 0x20) ? attr->attr | newattr : attr->attr & ~newattr; | ||
| 473 | } | 534 | } |
| 474 | } | 535 | } |
| 475 | 536 | ||
| 476 | /* draw arbitrary strings */ | 537 | /* draw arbitrary strings */ |
| 477 | static int | 538 | static int writescr(WINDOW *win, struct sb_entry *entry) { |
| 478 | writescr ( WINDOW *win, struct sb_entry *entry ) { | 539 | char tmp[64]; |
| 479 | char tmp [64]; | ||
| 480 | int charcount = 0; | 540 | int charcount = 0; |
| 481 | int i; | 541 | int i; |
| 482 | int textlen = strlen( entry->what ); | 542 | int textlen = strlen(entry->what); |
| 483 | int timelen = ((win == channel)||(win == private)) && usetime ? | 543 | int timelen = ((win == channel) || (win == private)) && usetime |
| 484 | (int)strftime(tmp,64,getformatstr(FS_TIME),localtime(&entry->when)) : 0; | 544 | ? (int)strftime(tmp, 64, getformatstr(FS_TIME), |
| 485 | char textbuffer[ textlen+timelen+1 ]; | 545 | localtime(&entry->when)) |
| 486 | ncurs_attr attrbuffer[ textlen+timelen+1 ]; | 546 | : 0; |
| 487 | ncurs_attr orgattr; | 547 | char textbuffer[textlen + timelen + 1]; |
| 548 | ncurs_attr attrbuffer[textlen + timelen + 1]; | ||
| 549 | ncurs_attr orgattr; | ||
| 488 | 550 | ||
| 489 | /* illegal window? return */ | 551 | /* illegal window? return */ |
| 490 | if( !win || !(textlen+timelen)) return 0; | 552 | if (!win || !(textlen + timelen)) |
| 553 | return 0; | ||
| 491 | 554 | ||
| 492 | /* store original attributes */ | 555 | /* store original attributes */ |
| 493 | WATTR_GET( win, orgattr); | 556 | WATTR_GET(win, orgattr); |
| 494 | attrbuffer[ 0 ] = orgattr; | 557 | attrbuffer[0] = orgattr; |
| 495 | 558 | ||
| 496 | /* copy time string */ | 559 | /* copy time string */ |
| 497 | for( i = 0; i < timelen; i++ ) | 560 | for (i = 0; i < timelen; i++) |
| 498 | if( tmp[ i ] == 1 ) { | 561 | if (tmp[i] == 1) { |
| 499 | docolorize( tmp[++i], attrbuffer+charcount, orgattr); | 562 | docolorize(tmp[++i], attrbuffer + charcount, orgattr); |
| 500 | } else { | 563 | } else { |
| 501 | attrbuffer[ charcount+1 ] = attrbuffer[ charcount ]; | 564 | attrbuffer[charcount + 1] = attrbuffer[charcount]; |
| 502 | textbuffer[ charcount++ ] = tmp[ i ]; | 565 | textbuffer[charcount++] = tmp[i]; |
| 503 | } | 566 | } |
| 504 | 567 | ||
| 505 | timelen = charcount; | 568 | timelen = charcount; |
| 506 | 569 | ||
| 507 | /* copy text */ | 570 | /* copy text */ |
| 508 | for( i = 0; i< textlen; i++ ) | 571 | for (i = 0; i < textlen; i++) |
| 509 | if( entry->what[ i ] == 1 ) { | 572 | if (entry->what[i] == 1) { |
| 510 | docolorize( entry->what[++i], attrbuffer+charcount, orgattr); | 573 | docolorize(entry->what[++i], attrbuffer + charcount, orgattr); |
| 511 | } else { | 574 | } else { |
| 512 | attrbuffer[ charcount+1 ] = attrbuffer[ charcount ]; | 575 | attrbuffer[charcount + 1] = attrbuffer[charcount]; |
| 513 | textbuffer[ charcount++ ] = entry->what[ i ]; | 576 | textbuffer[charcount++] = entry->what[i]; |
| 514 | } | 577 | } |
| 515 | 578 | ||
| 516 | /* zero terminate text --- very important :) */ | 579 | /* zero terminate text --- very important :) */ |
| 517 | textbuffer[ charcount ] = 0; | 580 | textbuffer[charcount] = 0; |
| 518 | 581 | ||
| 519 | /* hilite */ | 582 | /* hilite */ |
| 520 | if((win == channel)||(win == private)) { /* do not higlight bars */ | 583 | if ((win == channel) || (win == private)) { /* do not higlight bars */ |
| 521 | filt *flt = filterlist; | 584 | filt *flt = filterlist; |
| 522 | char *instr = textbuffer; | 585 | char *instr = textbuffer; |
| 523 | regmatch_t match; | 586 | regmatch_t match; |
| 524 | int j; | 587 | int j; |
| 525 | 588 | ||
| 526 | while( flt ) { | 589 | while (flt) { |
| 527 | if ( (flt->colour != '-') && (flt->colour != '+')) { | 590 | if ((flt->colour != '-') && (flt->colour != '+')) { |
| 528 | i = timelen; | 591 | i = timelen; |
| 529 | while( i < charcount ) { | 592 | while (i < charcount) { |
| 530 | if( regexec( &flt->regex, instr+i, 1, &match, 0 )) { | 593 | if (regexec(&flt->regex, instr + i, 1, &match, 0)) { |
| 531 | i = charcount; | 594 | i = charcount; |
| 532 | } else { | 595 | } else { |
| 533 | for( j = i + match.rm_so; j < i + match.rm_eo; j++) | 596 | for (j = i + match.rm_so; j < i + match.rm_eo; j++) |
| 534 | docolorize( flt->colour, attrbuffer+j, orgattr ); | 597 | docolorize(flt->colour, attrbuffer + j, orgattr); |
| 535 | i += 1 + match.rm_so; | 598 | i += 1 + match.rm_so; |
| 536 | } | ||
| 537 | } | ||
| 538 | } | 599 | } |
| 539 | flt = flt->next; | 600 | } |
| 540 | } | 601 | } |
| 602 | flt = flt->next; | ||
| 603 | } | ||
| 541 | } | 604 | } |
| 542 | 605 | ||
| 543 | if (getcurx(win)) waddch(win,'\n'); | 606 | if (getcurx(win)) |
| 544 | 607 | waddch(win, '\n'); | |
| 545 | for( i = 0; i < charcount; i++ ) { | 608 | |
| 546 | /* on start of line or attribute changes set new attribute */ | 609 | for (i = 0; i < charcount; i++) { |
| 547 | if( !i || memcmp( attrbuffer+i, attrbuffer+i-1, sizeof(ncurs_attr))) | 610 | /* on start of line or attribute changes set new attribute */ |
| 548 | WATTR_SET( win, attrbuffer[i]); | 611 | if (!i || memcmp(attrbuffer + i, attrbuffer + i - 1, sizeof(ncurs_attr))) |
| 549 | if( textbuffer[ i ] == ' ') { | 612 | WATTR_SET(win, attrbuffer[i]); |
| 550 | if ((calcdrawcus(textbuffer+i+1) + getcurx(win) > getmaxx(win) - 1 - 1)&& | 613 | if (textbuffer[i] == ' ') { |
| 551 | (calcdrawcus(textbuffer+i+1) < getmaxx(win) - 1 )) { | 614 | if ((calcdrawcus(textbuffer + i + 1) + getcurx(win) > |
| 552 | /* line wrap found */ | 615 | getmaxx(win) - 1 - 1) && |
| 553 | WATTR_SET( win, orgattr); | 616 | (calcdrawcus(textbuffer + i + 1) < getmaxx(win) - 1)) { |
| 554 | waddstr( win, "\n "); | 617 | /* line wrap found */ |
| 555 | WATTR_SET( win, attrbuffer[ i ]); | 618 | WATTR_SET(win, orgattr); |
| 556 | } | 619 | waddstr(win, "\n "); |
| 620 | WATTR_SET(win, attrbuffer[i]); | ||
| 557 | } | 621 | } |
| 558 | /* plot character */ | 622 | } |
| 559 | waddch( win, (unsigned char)textbuffer[ i ]); | 623 | /* plot character */ |
| 624 | waddch(win, (unsigned char)textbuffer[i]); | ||
| 560 | } | 625 | } |
| 561 | 626 | ||
| 562 | /* restore old attributes */ | 627 | /* restore old attributes */ |
| 563 | WATTR_SET (win, orgattr); | 628 | WATTR_SET(win, orgattr); |
| 564 | 629 | ||
| 565 | return charcount; | 630 | return charcount; |
| 566 | } | 631 | } |
| 567 | 632 | ||
| 568 | static void | 633 | static void resize_output() { |
| 569 | resize_output ( ) | 634 | int outputwidth = |
| 570 | { | 635 | (outputwidth_desired + 7 > screensx) ? screensx - 7 : outputwidth_desired; |
| 571 | int outputwidth = (outputwidth_desired + 7 > screensx) ? screensx - 7 : outputwidth_desired; | 636 | int outputheight = getsbdataheight(sb_out, outputwidth - 1, 0); |
| 572 | int outputheight = getsbdataheight(sb_out, outputwidth-1, 0); | ||
| 573 | 637 | ||
| 574 | if (outputheight + 5 > screensy ) outputheight = screensy - 5; | 638 | if (outputheight + 5 > screensy) |
| 575 | wresize(output,outputheight,outputwidth); | 639 | outputheight = screensy - 5; |
| 576 | mvwin(output,(screensy-outputheight)>>1,(screensx-outputwidth)>>1); | 640 | wresize(output, outputheight, outputwidth); |
| 641 | mvwin(output, (screensy - outputheight) >> 1, (screensx - outputwidth) >> 1); | ||
| 577 | drawwin(output, sb_out); | 642 | drawwin(output, sb_out); |
| 578 | } | 643 | } |
| 579 | 644 | ||
| 580 | static void | 645 | static void doscroll(int up) { |
| 581 | doscroll ( int up ) { | ||
| 582 | togglequery(); | 646 | togglequery(); |
| 583 | { | 647 | { |
| 584 | WINDOW *destwin = (sb_win && private) ? private : channel; | 648 | WINDOW *destwin = (sb_win && private) ? private : channel; |
| 585 | struct sb_data *sb = (sb_win && private) ? sb_priv : sb_pub; | 649 | struct sb_data *sb = (sb_win && private) ? sb_priv : sb_pub; |
| 586 | struct sb_entry *now = sb->entries, *prev = NULL, *tmp; | 650 | struct sb_entry *now = sb->entries, *prev = NULL, *tmp; |
| 587 | int lines = (getmaxy(destwin) - 1 ) >>1; | 651 | int lines = (getmaxy(destwin) - 1) >> 1; |
| 588 | 652 | ||
| 589 | if( sb->scroll != sb->count ) | 653 | if (sb->scroll != sb->count) |
| 590 | while( now && (now->id != sb->scroll) ) { | 654 | while (now && (now->id != sb->scroll)) { |
| 591 | tmp = now; now = (struct sb_entry*)((unsigned long)now->link ^ (unsigned long)prev); prev = tmp; | 655 | tmp = now; |
| 656 | now = | ||
| 657 | (struct sb_entry *)((unsigned long)now->link ^ (unsigned long)prev); | ||
| 658 | prev = tmp; | ||
| 592 | } | 659 | } |
| 593 | 660 | ||
| 594 | if( !up ) { | 661 | if (!up) { |
| 595 | prev = (struct sb_entry*)((unsigned long)now->link ^ (unsigned long)prev); | 662 | prev = |
| 596 | tmp = now; now = (struct sb_entry*)((unsigned long)now->link ^ (unsigned long)prev); prev = tmp; | 663 | (struct sb_entry *)((unsigned long)now->link ^ (unsigned long)prev); |
| 597 | } | 664 | tmp = now; |
| 665 | now = (struct sb_entry *)((unsigned long)now->link ^ (unsigned long)prev); | ||
| 666 | prev = tmp; | ||
| 667 | } | ||
| 598 | 668 | ||
| 599 | while( now && (lines > 0)) { | 669 | while (now && (lines > 0)) { |
| 600 | if ( (!filtertype) || ( (now->stamp != currentstamp) && ( (now->stamp == (currentstamp | (1<<15))) || testfilter( now ) ) ) ) | 670 | if ((!filtertype) || |
| 601 | { | 671 | ((now->stamp != currentstamp) && |
| 602 | lines -= getsbeheight( now, getmaxx(destwin) - 1, usetime ); | 672 | ((now->stamp == (currentstamp | (1 << 15))) || testfilter(now)))) { |
| 673 | lines -= getsbeheight(now, getmaxx(destwin) - 1, usetime); | ||
| 603 | } | 674 | } |
| 604 | tmp = now; now = (struct sb_entry*)((unsigned long)now->link ^ (unsigned long)prev); prev = tmp; | 675 | tmp = now; |
| 605 | } | 676 | now = (struct sb_entry *)((unsigned long)now->link ^ (unsigned long)prev); |
| 606 | if( now ) | 677 | prev = tmp; |
| 678 | } | ||
| 679 | if (now) | ||
| 607 | sb->scroll = now->id; | 680 | sb->scroll = now->id; |
| 608 | else | 681 | else if (!up) |
| 609 | if( !up ) sb->scroll = sb->count; | 682 | sb->scroll = sb->count; |
| 610 | 683 | ||
| 611 | drawwin(destwin, sb); | 684 | drawwin(destwin, sb); |
| 612 | wnoutrefresh(destwin); | 685 | wnoutrefresh(destwin); |
| 613 | 686 | ||
| 614 | togglequery(); | 687 | togglequery(); |
| 615 | 688 | ||
| 616 | if( private && (destwin == channel) ) | 689 | if (private && (destwin == channel)) |
| 617 | consoleline( NULL); | 690 | consoleline(NULL); |
| 618 | else | 691 | else |
| 619 | topicline( NULL); | 692 | topicline(NULL); |
| 620 | } | 693 | } |
| 621 | } | 694 | } |
| 622 | 695 | ||
| 696 | void scrollup(void) { doscroll(1); } | ||
| 623 | 697 | ||
| 624 | void | 698 | void scrolldown(void) { doscroll(0); } |
| 625 | scrollup (void) | ||
| 626 | { | ||
| 627 | doscroll( 1 ); | ||
| 628 | } | ||
| 629 | |||
| 630 | void | ||
| 631 | scrolldown (void) | ||
| 632 | { | ||
| 633 | doscroll( 0 ); | ||
| 634 | } | ||
| 635 | 699 | ||
| 636 | void | 700 | void scrollwin(void) { |
| 637 | scrollwin (void) | 701 | if (!sb_win && private && !privwinhidden) |
| 638 | { | 702 | sb_win = 1; |
| 639 | if (!sb_win && private && !privwinhidden) sb_win = 1; | 703 | else |
| 640 | else sb_win = 0; | 704 | sb_win = 0; |
| 641 | topicline(NULL); | 705 | topicline(NULL); |
| 642 | consoleline(NULL); | 706 | consoleline(NULL); |
| 643 | } | 707 | } |
| 644 | 708 | ||
| 645 | void | 709 | void growprivwin(void) { |
| 646 | growprivwin (void) { | 710 | if (private) { |
| 647 | if( private ) { | 711 | if (privwinhidden) |
| 648 | if( privwinhidden) | 712 | privwinhidden = 0; |
| 649 | privwinhidden = 0; | 713 | if (++privheight_desired > screensy - 5) |
| 650 | if( ++privheight_desired > screensy - 5) | 714 | privheight_desired = screensy - 5; |
| 651 | privheight_desired = screensy - 5; | 715 | resize(0); |
| 652 | resize(0); | 716 | } |
| 653 | } | ||
| 654 | } | 717 | } |
| 655 | 718 | ||
| 656 | void toggleprivwin (void) { | 719 | void toggleprivwin(void) { |
| 657 | if( outputshown ) { | 720 | if (outputshown) { |
| 658 | outputshown = 0; | 721 | outputshown = 0; |
| 659 | resize(0); | 722 | resize(0); |
| 660 | } else { | 723 | } else { |
| 661 | if( private ) { | 724 | if (private) { |
| 662 | if( privwinhidden ) { | 725 | if (privwinhidden) { |
| 663 | privheight_desired = privwinhidden; | 726 | privheight_desired = privwinhidden; |
| 664 | privwinhidden = 0; | 727 | privwinhidden = 0; |
| 665 | } else { | 728 | } else { |
| 666 | privwinhidden = privheight_desired; | 729 | privwinhidden = privheight_desired; |
| 667 | privheight_desired = 1; | 730 | privheight_desired = 1; |
| 668 | sb_win = 0; | 731 | sb_win = 0; |
| 669 | sb_priv->scroll = sb_priv->count; | 732 | sb_priv->scroll = sb_priv->count; |
| 670 | } | 733 | } |
| 671 | resize(0); | 734 | resize(0); |
| 672 | } | 735 | } |
| 673 | } | 736 | } |
| 674 | } | 737 | } |
| 675 | 738 | ||
| 676 | void | 739 | void shrinkprivwin(void) { |
| 677 | shrinkprivwin (void) { | 740 | if (private && !privwinhidden) { |
| 678 | if( private && !privwinhidden ) { | 741 | if (--privheight_desired < 1) |
| 679 | if( --privheight_desired < 1) privheight_desired = 1; | 742 | privheight_desired = 1; |
| 680 | if( privheight_desired > screensy - 5) privheight_desired = screensy - 5; | 743 | if (privheight_desired > screensy - 5) |
| 744 | privheight_desired = screensy - 5; | ||
| 681 | resize(0); | 745 | resize(0); |
| 682 | } | 746 | } |
| 683 | } | 747 | } |
| 684 | 748 | ||
| 685 | /* clear message window */ | 749 | /* clear message window */ |
| 686 | void | 750 | void clearpriv() { |
| 687 | clearpriv () | ||
| 688 | { | ||
| 689 | WINDOW *dest = NULL; | 751 | WINDOW *dest = NULL; |
| 690 | /* do we have a private window? */ | 752 | /* do we have a private window? */ |
| 691 | if (private && !privwinhidden ) | 753 | if (private && !privwinhidden) |
| 692 | dest = private; | 754 | dest = private; |
| 693 | else | 755 | else |
| 694 | dest = channel; | 756 | dest = channel; |
| 695 | 757 | ||
| 696 | /* clear window, move cursor to bottom, redraw */ | 758 | /* clear window, move cursor to bottom, redraw */ |
| 697 | wclear (dest); | 759 | wclear(dest); |
| 698 | wmove (dest, getmaxy(dest) - 1, getmaxx(dest) - 1); | 760 | wmove(dest, getmaxy(dest) - 1, getmaxx(dest) - 1); |
| 699 | wrefresh (dest); | 761 | wrefresh(dest); |
| 700 | |||
| 701 | } | 762 | } |
| 702 | 763 | ||
| 703 | /* clear channel window */ | 764 | /* clear channel window */ |
| 704 | void | 765 | void clearchan() { |
| 705 | clearchan () | ||
| 706 | { | ||
| 707 | /* clear window, move cursor to bottom, redraw */ | 766 | /* clear window, move cursor to bottom, redraw */ |
| 708 | wclear (channel); | 767 | wclear(channel); |
| 709 | wmove (channel, getmaxy(channel) - 1, getmaxx(channel) - 1); | 768 | wmove(channel, getmaxy(channel) - 1, getmaxx(channel) - 1); |
| 710 | wrefresh (channel); | 769 | wrefresh(channel); |
| 711 | } | 770 | } |
| 712 | 771 | ||
| 713 | /* Get window size */ | 772 | /* Get window size */ |
| 714 | 773 | ||
| 715 | void ttgtsz(int *x,int *y) { | 774 | void ttgtsz(int *x, int *y) { |
| 716 | #ifdef TIOCGSIZE | 775 | #ifdef TIOCGSIZE |
| 717 | struct ttysize getit; | 776 | struct ttysize getit; |
| 718 | #else | 777 | #else |
| 719 | #ifdef TIOCGWINSZ | 778 | #ifdef TIOCGWINSZ |
| 720 | struct winsize getit; | 779 | struct winsize getit; |
| 721 | #endif | 780 | #endif |
| 722 | #endif | 781 | #endif |
| 723 | 782 | ||
| 724 | *x=0; *y=0; | 783 | *x = 0; |
| 784 | *y = 0; | ||
| 725 | #ifdef TIOCGSIZE | 785 | #ifdef TIOCGSIZE |
| 726 | if(ioctl(1,TIOCGSIZE,&getit)!= -1) { | 786 | if (ioctl(1, TIOCGSIZE, &getit) != -1) { |
| 727 | *x=getit.ts_cols; | 787 | *x = getit.ts_cols; |
| 728 | *y=getit.ts_lines; | 788 | *y = getit.ts_lines; |
| 729 | } | 789 | } |
| 730 | #else | 790 | #else |
| 731 | #ifdef TIOCGWINSZ | 791 | #ifdef TIOCGWINSZ |
| 732 | if(ioctl(1,TIOCGWINSZ,&getit)!= -1) { | 792 | if (ioctl(1, TIOCGWINSZ, &getit) != -1) { |
| 733 | *x=getit.ws_col; | 793 | *x = getit.ws_col; |
| 734 | *y=getit.ws_row; | 794 | *y = getit.ws_row; |
| 735 | } | 795 | } |
| 736 | #endif | 796 | #endif |
| 737 | #endif | 797 | #endif |
| 738 | } | 798 | } |
| 739 | 799 | ||
| 740 | static void | 800 | static void forceredraw(void) { |
| 741 | forceredraw (void) | ||
| 742 | { | ||
| 743 | sb_pub->scroll = sb_pub->count; | 801 | sb_pub->scroll = sb_pub->count; |
| 744 | sb_priv->scroll = sb_priv->count; | 802 | sb_priv->scroll = sb_priv->count; |
| 745 | 803 | ||
| 746 | resize(0); | 804 | resize(0); |
| 747 | if(console) wclear(console); | 805 | if (console) |
| 748 | if(topic) wclear(topic); | 806 | wclear(console); |
| 749 | if(private) wclear(private); | 807 | if (topic) |
| 750 | if(channel) wclear(channel ); | 808 | wclear(topic); |
| 751 | if(output) wclear(output); | 809 | if (private) |
| 752 | if(input) wclear(input); | 810 | wclear(private); |
| 811 | if (channel) | ||
| 812 | wclear(channel); | ||
| 813 | if (output) | ||
| 814 | wclear(output); | ||
| 815 | if (input) | ||
| 816 | wclear(input); | ||
| 753 | resize(0); | 817 | resize(0); |
| 754 | |||
| 755 | } | 818 | } |
| 756 | 819 | ||
| 757 | /* resize display on SIGWINCH | 820 | /* resize display on SIGWINCH |
| 758 | Nowadays used as our main redraw trigger engine */ | 821 | Nowadays used as our main redraw trigger engine */ |
| 759 | void | 822 | void resize(int signal) { |
| 760 | resize (int signal) | 823 | int xsize, ysize, topicheight = topic ? 1 : 0; |
| 761 | { | 824 | (void)signal; |
| 762 | int xsize,ysize,topicheight=topic?1:0; | ||
| 763 | 825 | ||
| 764 | ttgtsz(&xsize,&ysize); | 826 | ttgtsz(&xsize, &ysize); |
| 765 | resizeterm(ysize,xsize); | 827 | resizeterm(ysize, xsize); |
| 766 | 828 | ||
| 767 | /* store screen-dimensions to local functions */ | 829 | /* store screen-dimensions to local functions */ |
| 768 | getmaxyx (stdscr, screensy, screensx); | 830 | getmaxyx(stdscr, screensy, screensx); |
| 769 | 831 | ||
| 770 | /* desired height of PM window is user controllable, | 832 | /* desired height of PM window is user controllable, |
| 771 | actual size depends on space available on screen */ | 833 | actual size depends on space available on screen */ |
| @@ -774,24 +836,34 @@ resize (int signal) | |||
| 774 | 836 | ||
| 775 | /* Leave at least 5 lines for input, console and | 837 | /* Leave at least 5 lines for input, console and |
| 776 | pubchannel */ | 838 | pubchannel */ |
| 777 | if ( privheight_desired > screensy - 5) | 839 | if (privheight_desired > screensy - 5) |
| 778 | privheight = screensy - 5; | 840 | privheight = screensy - 5; |
| 779 | else | 841 | else |
| 780 | privheight = privheight_desired; | 842 | privheight = privheight_desired; |
| 781 | 843 | ||
| 782 | /* check dimensions or bump user */ | 844 | /* check dimensions or bump user */ |
| 783 | if (screensy - privheight < 4) | 845 | if (screensy - privheight < 4) { |
| 784 | { | 846 | fprintf(stderr, |
| 785 | fprintf (stderr, "vchat-client: screen to short (only %d rows, at least %d expected), bailing out.\n", screensy, privheight + 6); | 847 | "vchat-client: screen to short (only %d rows, at least %d " |
| 786 | snprintf (errstr, ERRSTRSIZE, "vchat-client: screen to short (only %d rows, at least %d expected), bailing out.\n", screensy, privheight + 6); | 848 | "expected), bailing out.\n", |
| 787 | cleanup (0); | 849 | screensy, privheight + 6); |
| 788 | } | 850 | snprintf(errstr, ERRSTRSIZE, |
| 789 | if (screensx < 14) | 851 | "vchat-client: screen to short (only %d rows, at least %d " |
| 790 | { | 852 | "expected), bailing out.\n", |
| 791 | fprintf (stderr, "vchat-client: screen to thin (only %d cols, at least %d expected), bailing out.\n", screensx, 14); | 853 | screensy, privheight + 6); |
| 792 | snprintf (errstr, ERRSTRSIZE, "vchat-client: screen to thin (only %d cols, at least %d expected), bailing out.\n", screensx, 14); | 854 | cleanup(0); |
| 793 | cleanup (0); | 855 | } |
| 794 | } | 856 | if (screensx < 14) { |
| 857 | fprintf(stderr, | ||
| 858 | "vchat-client: screen to thin (only %d cols, at least %d " | ||
| 859 | "expected), bailing out.\n", | ||
| 860 | screensx, 14); | ||
| 861 | snprintf(errstr, ERRSTRSIZE, | ||
| 862 | "vchat-client: screen to thin (only %d cols, at least %d " | ||
| 863 | "expected), bailing out.\n", | ||
| 864 | screensx, 14); | ||
| 865 | cleanup(0); | ||
| 866 | } | ||
| 795 | 867 | ||
| 796 | /***** | 868 | /***** |
| 797 | * Arrange windows on screen | 869 | * Arrange windows on screen |
| @@ -800,38 +872,44 @@ resize (int signal) | |||
| 800 | togglequery(); | 872 | togglequery(); |
| 801 | 873 | ||
| 802 | /* console and input are always there and always 1 line tall */ | 874 | /* console and input are always there and always 1 line tall */ |
| 803 | wresize(console,1,screensx); | 875 | wresize(console, 1, screensx); |
| 804 | wresize(input,1,screensx); | 876 | wresize(input, 1, screensx); |
| 805 | 877 | ||
| 806 | /* If we got a private window and it is not hidden, set its size */ | 878 | /* If we got a private window and it is not hidden, set its size */ |
| 807 | if (private && !privwinhidden) | 879 | if (private && !privwinhidden) |
| 808 | wresize(private,privheight,screensx); | 880 | wresize(private, privheight, screensx); |
| 809 | 881 | ||
| 810 | /* If oldschool vchat is not enabled, we have a topic line */ | 882 | /* If oldschool vchat is not enabled, we have a topic line */ |
| 811 | if( topic ) | 883 | if (topic) |
| 812 | wresize(topic,1,screensx); | 884 | wresize(topic, 1, screensx); |
| 813 | 885 | ||
| 814 | /* public channel is always there and its height depends on: | 886 | /* public channel is always there and its height depends on: |
| 815 | * existence and visibility of priv window | 887 | * existence and visibility of priv window |
| 816 | * existence of a topic line (oldschool vchat style) | 888 | * existence of a topic line (oldschool vchat style) |
| 817 | */ | 889 | */ |
| 818 | wresize(channel, ( !private || privwinhidden ) ? screensy - ( topicheight + 2 ) : screensy - (privheight + ( topicheight + 2 )), screensx); | 890 | wresize(channel, |
| 891 | (!private || privwinhidden) | ||
| 892 | ? screensy - (topicheight + 2) | ||
| 893 | : screensy - (privheight + (topicheight + 2)), | ||
| 894 | screensx); | ||
| 819 | 895 | ||
| 820 | /* Console and input alway take bottommost lines */ | 896 | /* Console and input alway take bottommost lines */ |
| 821 | mvwin(console,screensy-2,0); | 897 | mvwin(console, screensy - 2, 0); |
| 822 | mvwin(input,screensy-1,0); | 898 | mvwin(input, screensy - 1, 0); |
| 823 | 899 | ||
| 824 | /* Private window always is top left */ | 900 | /* Private window always is top left */ |
| 825 | if(private && !privwinhidden) | 901 | if (private && !privwinhidden) |
| 826 | mvwin(private,0,0); | 902 | mvwin(private, 0, 0); |
| 827 | 903 | ||
| 828 | /* Topic window may not exist without priv window, so it is | 904 | /* Topic window may not exist without priv window, so it is |
| 829 | safe to assume sane values for privwinhidden and privheight */ | 905 | safe to assume sane values for privwinhidden and privheight */ |
| 830 | if( topic ) | 906 | if (topic) |
| 831 | mvwin(topic,privwinhidden ? 0 : privheight, 0); | 907 | mvwin(topic, privwinhidden ? 0 : privheight, 0); |
| 832 | 908 | ||
| 833 | /* chan window starts below private window and topic line */ | 909 | /* chan window starts below private window and topic line */ |
| 834 | mvwin(channel, ( !private || privwinhidden ) ? topicheight : privheight + topicheight, 0); | 910 | mvwin(channel, |
| 911 | (!private || privwinhidden) ? topicheight : privheight + topicheight, | ||
| 912 | 0); | ||
| 835 | 913 | ||
| 836 | /******* | 914 | /******* |
| 837 | * Now actual redraw starts, note, that we only fill | 915 | * Now actual redraw starts, note, that we only fill |
| @@ -844,96 +922,106 @@ resize (int signal) | |||
| 844 | ******/ | 922 | ******/ |
| 845 | 923 | ||
| 846 | /* pub channel is always there, paint scrollback buffers */ | 924 | /* pub channel is always there, paint scrollback buffers */ |
| 847 | drawwin(channel, sb_pub); | 925 | drawwin(channel, sb_pub); |
| 848 | /* if priv exists and is visible, paint scrollback buffers */ | 926 | /* if priv exists and is visible, paint scrollback buffers */ |
| 849 | if(private && !privwinhidden ) | 927 | if (private && !privwinhidden) |
| 850 | drawwin(private, sb_priv); | 928 | drawwin(private, sb_priv); |
| 851 | /* Send window's contents to curses virtual buffers */ | 929 | /* Send window's contents to curses virtual buffers */ |
| 852 | wnoutrefresh(channel); | 930 | wnoutrefresh(channel); |
| 853 | if(private && !privwinhidden ) | 931 | if (private && !privwinhidden) |
| 854 | wnoutrefresh(private); | 932 | wnoutrefresh(private); |
| 855 | 933 | ||
| 856 | togglequery(); | 934 | togglequery(); |
| 857 | 935 | ||
| 858 | /* Resize and draw our message window, render topic and | 936 | /* Resize and draw our message window, render topic and |
| 859 | console line */ | 937 | console line */ |
| 860 | if(outputshown) resize_output(); | 938 | if (outputshown) |
| 861 | if(topic) topicline(NULL); | 939 | resize_output(); |
| 862 | consoleline(NULL); | 940 | if (topic) |
| 863 | if(loggedin) vciredraw(); | 941 | topicline(NULL); |
| 942 | consoleline(NULL); | ||
| 943 | if (loggedin) | ||
| 944 | vciredraw(); | ||
| 864 | } | 945 | } |
| 865 | 946 | ||
| 866 | static int | 947 | static int gettextwidth(const char *textbuffer) { |
| 867 | gettextwidth (const char *textbuffer) | 948 | int width = 0; |
| 868 | { | ||
| 869 | int width = 0; | ||
| 870 | 949 | ||
| 871 | do switch( *(textbuffer++) ) { | 950 | do |
| 872 | case 1: | 951 | switch (*(textbuffer++)) { |
| 873 | if (!*(textbuffer++)) return width; | 952 | case 1: |
| 874 | break; | 953 | if (!*(textbuffer++)) |
| 875 | case 0: | 954 | return width; |
| 955 | break; | ||
| 956 | case 0: | ||
| 876 | return width; | 957 | return width; |
| 877 | break; | 958 | break; |
| 878 | default: | 959 | default: |
| 879 | width++; | 960 | width++; |
| 880 | break; | 961 | break; |
| 881 | } while( 1 ); | 962 | } |
| 963 | while (1); | ||
| 882 | } | 964 | } |
| 883 | 965 | ||
| 884 | static int | 966 | static int getsbdataheight(struct sb_data *data, const int xwidth, |
| 885 | getsbdataheight (struct sb_data *data, const int xwidth, int needstime ) | 967 | int needstime) { |
| 886 | { | ||
| 887 | struct sb_entry *now = data->entries, *prev = NULL, *tmp; | 968 | struct sb_entry *now = data->entries, *prev = NULL, *tmp; |
| 888 | int height = 0; | 969 | int height = 0; |
| 889 | while( now ) { | 970 | while (now) { |
| 890 | height += getsbeheight( now, xwidth, needstime); | 971 | height += getsbeheight(now, xwidth, needstime); |
| 891 | tmp = now; now = (struct sb_entry*)((unsigned long)now->link ^ (unsigned long)prev); prev = tmp; | 972 | tmp = now; |
| 973 | now = (struct sb_entry *)((unsigned long)now->link ^ (unsigned long)prev); | ||
| 974 | prev = tmp; | ||
| 892 | } | 975 | } |
| 893 | return height; | 976 | return height; |
| 894 | } | 977 | } |
| 895 | 978 | ||
| 896 | static int | 979 | static int getsbeheight(struct sb_entry *entry, const int xwidth, |
| 897 | getsbeheight (struct sb_entry *entry, const int xwidth, int needstime ) | 980 | int needstime) { |
| 898 | { | ||
| 899 | int curx = 0, lines = 1; | 981 | int curx = 0, lines = 1; |
| 900 | char tmp[ 64 ], *textbuffer; | 982 | char tmp[64], *textbuffer; |
| 901 | 983 | ||
| 902 | if( needstime ) { | 984 | if (needstime) { |
| 903 | int timelen = (int)strftime(tmp,64,getformatstr(FS_TIME),localtime(&entry->when)); | 985 | int timelen = |
| 904 | tmp[ timelen ] = 2; | 986 | (int)strftime(tmp, 64, getformatstr(FS_TIME), localtime(&entry->when)); |
| 905 | textbuffer=tmp; | 987 | tmp[timelen] = 2; |
| 988 | textbuffer = tmp; | ||
| 906 | } else { | 989 | } else { |
| 907 | textbuffer = entry->what; | 990 | textbuffer = entry->what; |
| 908 | } | 991 | } |
| 909 | 992 | ||
| 910 | do switch( *textbuffer++ ) { | 993 | do |
| 911 | case ' ': | 994 | switch (*textbuffer++) { |
| 995 | case ' ': | ||
| 912 | if ((calcdrawcus(textbuffer) + curx > xwidth - 1) && | 996 | if ((calcdrawcus(textbuffer) + curx > xwidth - 1) && |
| 913 | (calcdrawcus(textbuffer) < xwidth)) { | 997 | (calcdrawcus(textbuffer) < xwidth)) { |
| 914 | lines++; curx = 4; | 998 | lines++; |
| 915 | } else { | 999 | curx = 4; |
| 916 | if( curx++ == xwidth ) { | 1000 | } else { |
| 917 | curx = 0; lines++; | 1001 | if (curx++ == xwidth) { |
| 918 | } | 1002 | curx = 0; |
| 919 | } | 1003 | lines++; |
| 1004 | } | ||
| 1005 | } | ||
| 920 | break; | 1006 | break; |
| 921 | case 1: | 1007 | case 1: |
| 922 | if (!*textbuffer++) return lines; | 1008 | if (!*textbuffer++) |
| 923 | break; | 1009 | return lines; |
| 924 | case 0: | 1010 | break; |
| 1011 | case 0: | ||
| 925 | return lines; | 1012 | return lines; |
| 926 | break; | 1013 | break; |
| 927 | case 2: | 1014 | case 2: |
| 928 | textbuffer=entry->what; | 1015 | textbuffer = entry->what; |
| 929 | break; | 1016 | break; |
| 930 | default: | 1017 | default: |
| 931 | if( curx++ == xwidth ) { | 1018 | if (curx++ == xwidth) { |
| 932 | curx = 0; lines++; | 1019 | curx = 0; |
| 1020 | lines++; | ||
| 933 | } | 1021 | } |
| 934 | break; | 1022 | break; |
| 935 | } while( 1 ); | 1023 | } |
| 936 | 1024 | while (1); | |
| 937 | } | 1025 | } |
| 938 | 1026 | ||
| 939 | /* Check, which kind of filter we have to apply: | 1027 | /* Check, which kind of filter we have to apply: |
| @@ -943,8 +1031,7 @@ getsbeheight (struct sb_entry *entry, const int xwidth, int needstime ) | |||
| 943 | If no, or only colouring rules have been found, | 1031 | If no, or only colouring rules have been found, |
| 944 | no line filtering applies. | 1032 | no line filtering applies. |
| 945 | */ | 1033 | */ |
| 946 | static int | 1034 | static int analyzefilters(void) { |
| 947 | analyzefilters( void ) { | ||
| 948 | filt *filters = filterlist; | 1035 | filt *filters = filterlist; |
| 949 | int type = 0; | 1036 | int type = 0; |
| 950 | 1037 | ||
| @@ -961,96 +1048,106 @@ analyzefilters( void ) { | |||
| 961 | tested the line against. This Stamp | 1048 | tested the line against. This Stamp |
| 962 | is updated for each change to the | 1049 | is updated for each change to the |
| 963 | filter list */ | 1050 | filter list */ |
| 964 | if( ++currentstamp == 0x3fff ) currentstamp = 1; | 1051 | if (++currentstamp == 0x3fff) |
| 965 | 1052 | currentstamp = 1; | |
| 966 | while( (type!=1) && filters ) { | 1053 | |
| 967 | if( filters->colour == '-' ) type = 2; | 1054 | while ((type != 1) && filters) { |
| 968 | if( filters->colour == '+' ) type = 1; | 1055 | if (filters->colour == '-') |
| 969 | filters=filters->next; | 1056 | type = 2; |
| 1057 | if (filters->colour == '+') | ||
| 1058 | type = 1; | ||
| 1059 | filters = filters->next; | ||
| 970 | } | 1060 | } |
| 971 | return type; | 1061 | return type; |
| 972 | } | 1062 | } |
| 973 | 1063 | ||
| 974 | static int | 1064 | static int testfilter(struct sb_entry *entry) { |
| 975 | testfilter ( struct sb_entry* entry ) { | 1065 | int match = 0; |
| 976 | int match = 0; | 1066 | filt *filters = filterlist; |
| 977 | filt *filters = filterlist; | 1067 | char filtercolour = filtertype == 2 ? '-' : '+'; |
| 978 | char filtercolour = filtertype == 2 ? '-' : '+'; | ||
| 979 | 1068 | ||
| 980 | while( !match && filters ) { | 1069 | while (!match && filters) { |
| 981 | if( filters->colour == filtercolour ) | 1070 | if (filters->colour == filtercolour) |
| 982 | match = regexec( &filters->regex, entry->what, 0, NULL, 0 ) ? 0 : 1; | 1071 | match = regexec(&filters->regex, entry->what, 0, NULL, 0) ? 0 : 1; |
| 983 | filters=filters->next; | 1072 | filters = filters->next; |
| 984 | } | 1073 | } |
| 985 | match = ( filtertype == 2 ) ? ( 1 - match ) : match; | 1074 | match = (filtertype == 2) ? (1 - match) : match; |
| 986 | entry->stamp = (match << 15) | currentstamp; | 1075 | entry->stamp = (match << 15) | currentstamp; |
| 987 | 1076 | ||
| 988 | return match; | 1077 | return match; |
| 989 | } | 1078 | } |
| 990 | 1079 | ||
| 991 | static void | 1080 | static void drawwin(WINDOW *win, struct sb_data *sb) { |
| 992 | drawwin (WINDOW *win, struct sb_data *sb ) | ||
| 993 | { | ||
| 994 | if (win) { | 1081 | if (win) { |
| 995 | struct sb_entry *now = sb->entries, *prev = NULL, *tmp; | 1082 | struct sb_entry *now = sb->entries, *prev = NULL, *tmp; |
| 996 | struct sb_entry *vis[getmaxy(win)]; | 1083 | struct sb_entry *vis[getmaxy(win)]; |
| 997 | int sumlines = 0, sumbuffers = 0; | 1084 | int sumlines = 0, sumbuffers = 0; |
| 998 | 1085 | ||
| 999 | /* public scrollback */ | 1086 | /* public scrollback */ |
| 1000 | if( sb->scroll != sb->count ) | 1087 | if (sb->scroll != sb->count) |
| 1001 | while( now && (now->id != sb->scroll) ) { | 1088 | while (now && (now->id != sb->scroll)) { |
| 1002 | tmp = now; now = (struct sb_entry*)((unsigned long)now->link ^ (unsigned long)prev); prev = tmp; | 1089 | tmp = now; |
| 1003 | } | 1090 | now = |
| 1091 | (struct sb_entry *)((unsigned long)now->link ^ (unsigned long)prev); | ||
| 1092 | prev = tmp; | ||
| 1093 | } | ||
| 1004 | 1094 | ||
| 1005 | if( (win == output) || (filtertype == 0)) { | 1095 | if ((win == output) || (filtertype == 0)) { |
| 1006 | while( now && (sumlines <= getmaxy(win) - 1 )) { | 1096 | while (now && (sumlines <= getmaxy(win) - 1)) { |
| 1007 | sumlines += getsbeheight( now, getmaxx(win) - 1, ((win == channel)||(win == private)) && usetime ); | 1097 | sumlines += |
| 1008 | vis[ sumbuffers++ ] = now; | 1098 | getsbeheight(now, getmaxx(win) - 1, |
| 1009 | tmp = now; now = (struct sb_entry*)((unsigned long)now->link ^ (unsigned long)prev); prev = tmp; | 1099 | ((win == channel) || (win == private)) && usetime); |
| 1010 | } | 1100 | vis[sumbuffers++] = now; |
| 1011 | } else { | 1101 | tmp = now; |
| 1012 | while( now && (sumlines <= getmaxy(win) - 1 )) { | 1102 | now = |
| 1013 | 1103 | (struct sb_entry *)((unsigned long)now->link ^ (unsigned long)prev); | |
| 1014 | /* If stamp matches exactly, line has been filtered out, since top | 1104 | prev = tmp; |
| 1015 | bit off means hidden */ | 1105 | } |
| 1016 | if( now->stamp != currentstamp) { | 1106 | } else { |
| 1017 | 1107 | while (now && (sumlines <= getmaxy(win) - 1)) { | |
| 1018 | /* If stamp matches and has top bit set, it has been identified | 1108 | |
| 1019 | positively. Else stamp does not match and line has to be | 1109 | /* If stamp matches exactly, line has been filtered out, since top |
| 1020 | tested against filters, which updates stamp. */ | 1110 | bit off means hidden */ |
| 1021 | if( (now->stamp == (currentstamp | 0x8000) ) || testfilter( now )) | 1111 | if (now->stamp != currentstamp) { |
| 1022 | { | 1112 | |
| 1023 | sumlines += getsbeheight( now, getmaxx(win) - 1, ((win == channel)||(win == private)) && usetime ); | 1113 | /* If stamp matches and has top bit set, it has been identified |
| 1024 | vis[ sumbuffers++ ] = now; | 1114 | positively. Else stamp does not match and line has to be |
| 1025 | } | 1115 | tested against filters, which updates stamp. */ |
| 1026 | 1116 | if ((now->stamp == (currentstamp | 0x8000)) || testfilter(now)) { | |
| 1027 | } | 1117 | sumlines += |
| 1028 | tmp = now; now = (struct sb_entry*)((unsigned long)now->link ^ (unsigned long)prev); prev = tmp; | 1118 | getsbeheight(now, getmaxx(win) - 1, |
| 1119 | ((win == channel) || (win == private)) && usetime); | ||
| 1120 | vis[sumbuffers++] = now; | ||
| 1029 | } | 1121 | } |
| 1122 | } | ||
| 1123 | tmp = now; | ||
| 1124 | now = | ||
| 1125 | (struct sb_entry *)((unsigned long)now->link ^ (unsigned long)prev); | ||
| 1126 | prev = tmp; | ||
| 1030 | } | 1127 | } |
| 1128 | } | ||
| 1031 | 1129 | ||
| 1032 | /* If buffer is not completely filled, clear window */ | 1130 | /* If buffer is not completely filled, clear window */ |
| 1033 | if( sumlines < getmaxy(win) ) | 1131 | if (sumlines < getmaxy(win)) |
| 1034 | wclear(win); | 1132 | wclear(win); |
| 1035 | 1133 | ||
| 1036 | /* Move pointer to bottom to let curses scroll */ | 1134 | /* Move pointer to bottom to let curses scroll */ |
| 1037 | wmove(win, getmaxy(win) - 1, getmaxx(win) - 1); | 1135 | wmove(win, getmaxy(win) - 1, getmaxx(win) - 1); |
| 1038 | 1136 | ||
| 1039 | /* Plot visible lines */ | 1137 | /* Plot visible lines */ |
| 1040 | while (sumbuffers--) writescr( win, vis[sumbuffers] ); | 1138 | while (sumbuffers--) |
| 1139 | writescr(win, vis[sumbuffers]); | ||
| 1041 | } | 1140 | } |
| 1042 | } | 1141 | } |
| 1043 | 1142 | ||
| 1044 | /* initialize curses and display */ | 1143 | /* initialize curses and display */ |
| 1045 | void | 1144 | void initui(void) { |
| 1046 | initui (void) | ||
| 1047 | { | ||
| 1048 | Keymap keymap; | 1145 | Keymap keymap; |
| 1049 | 1146 | ||
| 1050 | /* init curses */ | 1147 | /* init curses */ |
| 1051 | if (!ui_init) { | 1148 | if (!ui_init) { |
| 1052 | initscr (); | 1149 | initscr(); |
| 1053 | ui_init = 1; | 1150 | ui_init = 1; |
| 1054 | } | 1151 | } |
| 1055 | 1152 | ||
| 1056 | /* install signalhandler */ | 1153 | /* install signalhandler */ |
| @@ -1059,119 +1156,146 @@ initui (void) | |||
| 1059 | signal(SIGCONT, forceredraw_wrapper); | 1156 | signal(SIGCONT, forceredraw_wrapper); |
| 1060 | 1157 | ||
| 1061 | /* set options */ | 1158 | /* set options */ |
| 1062 | keypad (stdscr, TRUE); | 1159 | keypad(stdscr, TRUE); |
| 1063 | nonl (); | 1160 | nonl(); |
| 1064 | cbreak (); | 1161 | cbreak(); |
| 1065 | 1162 | ||
| 1066 | /* color or monochome display? */ | 1163 | /* color or monochome display? */ |
| 1067 | if (has_colors ()) | 1164 | if (has_colors()) { |
| 1068 | { | 1165 | /* start color and set a colorset */ |
| 1069 | /* start color and set a colorset */ | 1166 | start_color(); |
| 1070 | start_color (); | 1167 | use_default_colors(); |
| 1071 | use_default_colors(); | 1168 | init_pair(1, COLOR_RED, -1); |
| 1072 | init_pair (1, COLOR_RED, -1); | 1169 | init_pair(2, COLOR_GREEN, -1); |
| 1073 | init_pair (2, COLOR_GREEN, -1); | 1170 | init_pair(3, COLOR_YELLOW, -1); |
| 1074 | init_pair (3, COLOR_YELLOW, -1); | 1171 | init_pair(4, COLOR_BLUE, -1); |
| 1075 | init_pair (4, COLOR_BLUE, -1); | 1172 | init_pair(5, COLOR_MAGENTA, -1); |
| 1076 | init_pair (5, COLOR_MAGENTA, -1); | 1173 | init_pair(6, COLOR_CYAN, -1); |
| 1077 | init_pair (6, COLOR_CYAN, -1); | 1174 | init_pair(7, COLOR_WHITE, -1); |
| 1078 | init_pair (7, COLOR_WHITE, -1); | 1175 | init_pair(8, COLOR_WHITE, COLOR_RED); |
| 1079 | init_pair (8, COLOR_WHITE, COLOR_RED); | 1176 | init_pair(9, COLOR_WHITE, COLOR_BLUE); |
| 1080 | init_pair (9, COLOR_WHITE, COLOR_BLUE); | 1177 | init_pair(10, COLOR_WHITE, COLOR_BLACK); |
| 1081 | } | 1178 | } else { |
| 1082 | else | 1179 | /* monochrome, start color and set a colorset anyways */ |
| 1083 | { | 1180 | start_color(); |
| 1084 | /* monochrome, start color and set a colorset anyways */ | 1181 | init_pair(1, -1, -1); |
| 1085 | start_color (); | 1182 | init_pair(2, -1, -1); |
| 1086 | init_pair (1, -1, -1); | 1183 | init_pair(3, -1, -1); |
| 1087 | init_pair (2, -1, -1); | 1184 | init_pair(4, -1, -1); |
| 1088 | init_pair (3, -1, -1); | 1185 | init_pair(5, -1, -1); |
| 1089 | init_pair (4, -1, -1); | 1186 | init_pair(6, -1, -1); |
| 1090 | init_pair (5, -1, -1); | 1187 | init_pair(7, -1, -1); |
| 1091 | init_pair (6, -1, -1); | 1188 | init_pair(8, -1, -1); |
| 1092 | init_pair (7, -1, -1); | 1189 | init_pair(9, -1, -1); |
| 1093 | init_pair (8, -1, -1); | 1190 | init_pair(10, -1, -1); |
| 1094 | init_pair (9, -1, -1); | 1191 | } |
| 1095 | } | ||
| 1096 | 1192 | ||
| 1097 | /* store screen-dimensions to local functions */ | 1193 | /* store screen-dimensions to local functions */ |
| 1098 | getmaxyx (stdscr, screensy, screensx); | 1194 | getmaxyx(stdscr, screensy, screensx); |
| 1099 | 1195 | ||
| 1100 | if (!privheight_desired) privheight_desired = getintoption(CF_PRIVHEIGHT); | 1196 | if (!privheight_desired) |
| 1101 | if ( privheight_desired > screensy - 5) privheight = screensy - 5; else privheight = privheight_desired; | 1197 | privheight_desired = getintoption(CF_PRIVHEIGHT); |
| 1198 | if (privheight_desired > screensy - 5) | ||
| 1199 | privheight = screensy - 5; | ||
| 1200 | else | ||
| 1201 | privheight = privheight_desired; | ||
| 1102 | 1202 | ||
| 1103 | /* check dimensions or bump user */ | 1203 | /* check dimensions or bump user */ |
| 1104 | if (screensy - privheight < 4) | 1204 | if (screensy - privheight < 4) { |
| 1105 | { | 1205 | fprintf(stderr, |
| 1106 | fprintf (stderr, "vchat-client: screen to short (only %d rows, at least %d expected), bailing out.\n", screensy, privheight + 6); | 1206 | "vchat-client: screen to short (only %d rows, at least %d " |
| 1107 | snprintf (errstr, ERRSTRSIZE, "vchat-client: screen to short (only %d rows, at least %d expected), bailing out.\n", screensy, privheight + 6); | 1207 | "expected), bailing out.\n", |
| 1108 | cleanup (0); | 1208 | screensy, privheight + 6); |
| 1109 | } | 1209 | snprintf(errstr, ERRSTRSIZE, |
| 1110 | if (screensx < 14) | 1210 | "vchat-client: screen to short (only %d rows, at least %d " |
| 1111 | { | 1211 | "expected), bailing out.\n", |
| 1112 | fprintf (stderr, "vchat-client: screen to thin (only %d cols, at least %d expected), bailing out.\n", screensx, 14); | 1212 | screensy, privheight + 6); |
| 1113 | snprintf (errstr, ERRSTRSIZE, "vchat-client: screen to thin (only %d cols, at least %d expected), bailing out.\n", screensx, 14); | 1213 | cleanup(0); |
| 1114 | cleanup (0); | 1214 | } |
| 1115 | } | 1215 | if (screensx < 14) { |
| 1216 | fprintf(stderr, | ||
| 1217 | "vchat-client: screen to thin (only %d cols, at least %d " | ||
| 1218 | "expected), bailing out.\n", | ||
| 1219 | screensx, 14); | ||
| 1220 | snprintf(errstr, ERRSTRSIZE, | ||
| 1221 | "vchat-client: screen to thin (only %d cols, at least %d " | ||
| 1222 | "expected), bailing out.\n", | ||
| 1223 | screensx, 14); | ||
| 1224 | cleanup(0); | ||
| 1225 | } | ||
| 1116 | 1226 | ||
| 1117 | /* setup our windows */ | 1227 | /* setup our windows */ |
| 1118 | console = newwin (1, screensx, screensy - 2, 0); | 1228 | console = newwin(1, screensx, screensy - 2, 0); |
| 1119 | input = newwin (1, screensx, screensy - 1, 0); | 1229 | input = newwin(1, screensx, screensy - 1, 0); |
| 1120 | if (privheight) private = newwin (privheight, screensx, 0, 0); | 1230 | if (privheight) |
| 1121 | if( private || getintoption(CF_USETOPIC)) | 1231 | private |
| 1122 | topic = newwin (1, screensx, privheight, 0); | 1232 | = newwin(privheight, screensx, 0, 0); |
| 1123 | channel = newwin (screensy - (privheight+3), screensx, (privheight+1), 0); | 1233 | if (private || getintoption(CF_USETOPIC)) |
| 1124 | output = newwin (1, screensx, 1, 0); | 1234 | topic = newwin(1, screensx, privheight, 0); |
| 1235 | channel = newwin(screensy - (privheight + 3), screensx, (privheight + 1), 0); | ||
| 1236 | output = newwin(1, screensx, 1, 0); | ||
| 1125 | 1237 | ||
| 1126 | /* promblems opening windows? bye! */ | 1238 | /* promblems opening windows? bye! */ |
| 1127 | if (!console || !input || (!topic && getintoption(CF_USETOPIC))|| !channel || !output || ( !private && privheight )) | 1239 | if (!console || !input || (!topic && getintoption(CF_USETOPIC)) || !channel || |
| 1128 | { | 1240 | !output || (!private && privheight)) { |
| 1129 | fprintf (stderr, "vchat-client: could not open windows, bailing out.\n"); | 1241 | fprintf(stderr, "vchat-client: could not open windows, bailing out.\n"); |
| 1130 | cleanup (0); | 1242 | cleanup(0); |
| 1131 | } | 1243 | } |
| 1132 | 1244 | ||
| 1133 | /* Prepare our scrollback buffers */ | 1245 | /* Prepare our scrollback buffers */ |
| 1134 | sb_pub = (struct sb_data*)malloc( sizeof(struct sb_data)); | 1246 | sb_pub = (struct sb_data *)malloc(sizeof(struct sb_data)); |
| 1135 | sb_out = (struct sb_data*)malloc( sizeof(struct sb_data)); | 1247 | sb_out = (struct sb_data *)malloc(sizeof(struct sb_data)); |
| 1136 | if( privheight) | 1248 | sb_connect = (struct sb_data *)malloc(sizeof(struct sb_data)); |
| 1137 | sb_priv = (struct sb_data*)malloc( sizeof(struct sb_data)); | 1249 | if (privheight) |
| 1250 | sb_priv = (struct sb_data *)malloc(sizeof(struct sb_data)); | ||
| 1138 | else | 1251 | else |
| 1139 | sb_priv = sb_pub; | 1252 | sb_priv = sb_pub; |
| 1140 | 1253 | ||
| 1141 | memset( sb_pub, 0, sizeof(struct sb_data)); | 1254 | memset(sb_pub, 0, sizeof(struct sb_data)); |
| 1142 | memset( sb_priv, 0, sizeof(struct sb_data)); | 1255 | memset(sb_priv, 0, sizeof(struct sb_data)); |
| 1143 | memset( sb_out, 0, sizeof(struct sb_data)); | 1256 | memset(sb_out, 0, sizeof(struct sb_data)); |
| 1257 | memset(sb_connect, 0, sizeof(struct sb_data)); | ||
| 1144 | 1258 | ||
| 1145 | /* set colors for windows */ | 1259 | /* set colors for windows */ |
| 1146 | if (has_colors()) { | 1260 | if (has_colors()) { |
| 1147 | wattrset (console, COLOR_PAIR (9)); | 1261 | if (getintoption(CF_INVWINBAR)) { |
| 1148 | wattrset (input, COLOR_PAIR (0)); | 1262 | wbkgd(console, COLOR_PAIR(0)); |
| 1149 | wbkgd (output, COLOR_PAIR(8)); | 1263 | wattron(console, A_REVERSE); |
| 1150 | wbkgd (console, COLOR_PAIR (9)); | 1264 | } else { |
| 1151 | wbkgd (channel, COLOR_PAIR (0)); | 1265 | wattrset(console, COLOR_PAIR(9)); |
| 1152 | wbkgd (input, COLOR_PAIR (0)); | 1266 | wbkgd(console, COLOR_PAIR(9)); |
| 1153 | if (private) | 1267 | } |
| 1154 | wbkgd (private, COLOR_PAIR (0)); | 1268 | wattrset(input, COLOR_PAIR(0)); |
| 1155 | if( topic ) { | 1269 | wbkgd(output, COLOR_PAIR(8)); |
| 1156 | wattrset (topic, COLOR_PAIR (9)); | 1270 | wbkgd(channel, COLOR_PAIR(0)); |
| 1157 | wbkgd (topic, COLOR_PAIR (9)); | 1271 | wbkgd(input, COLOR_PAIR(0)); |
| 1158 | } | 1272 | if (private) |
| 1273 | wbkgd(private, COLOR_PAIR(0)); | ||
| 1274 | if (topic) { | ||
| 1275 | if (getintoption(CF_INVWINBAR)) { | ||
| 1276 | wbkgd(input, COLOR_PAIR(0)); | ||
| 1277 | wattron(topic, A_REVERSE); | ||
| 1278 | } else { | ||
| 1279 | wattrset(topic, COLOR_PAIR(9)); | ||
| 1280 | wbkgd(topic, COLOR_PAIR(9)); | ||
| 1281 | } | ||
| 1282 | } | ||
| 1159 | } else { | 1283 | } else { |
| 1160 | wattron (console, A_REVERSE); | 1284 | wattron(console, A_REVERSE); |
| 1161 | wattron (output, A_REVERSE); | 1285 | wattron(output, A_REVERSE); |
| 1162 | wbkgd(output, A_REVERSE); | 1286 | wbkgd(output, A_REVERSE); |
| 1163 | if( topic ) | 1287 | if (topic) |
| 1164 | wattron (topic, A_REVERSE); | 1288 | wattron(topic, A_REVERSE); |
| 1165 | } | 1289 | } |
| 1166 | 1290 | ||
| 1167 | /* set some options */ | 1291 | /* set some options */ |
| 1168 | scrollok (channel, TRUE); | 1292 | scrollok(channel, TRUE); |
| 1169 | if (private) | 1293 | if (private) |
| 1170 | scrollok (private, TRUE); | 1294 | scrollok(private, TRUE); |
| 1171 | scrollok (input, TRUE); | 1295 | scrollok(input, TRUE); |
| 1172 | scrollok (output, TRUE); | 1296 | scrollok(output, TRUE); |
| 1173 | //idlok(channel,TRUE); | 1297 | // idlok(channel,TRUE); |
| 1174 | wtimeout (input, 100); | 1298 | wtimeout(input, 100); |
| 1175 | 1299 | ||
| 1176 | /* setup readlines display */ | 1300 | /* setup readlines display */ |
| 1177 | /* FIXME: present only in newer readline versions | 1301 | /* FIXME: present only in newer readline versions |
| @@ -1182,87 +1306,88 @@ initui (void) | |||
| 1182 | rl_redisplay_function = vciredraw; | 1306 | rl_redisplay_function = vciredraw; |
| 1183 | 1307 | ||
| 1184 | /* get keymap, throw out unwanted binding */ | 1308 | /* get keymap, throw out unwanted binding */ |
| 1185 | keymap = rl_get_keymap (); | 1309 | keymap = rl_get_keymap(); |
| 1186 | rl_unbind_command_in_map ("clear-screen", keymap); | 1310 | rl_unbind_command_in_map("clear-screen", keymap); |
| 1187 | rl_unbind_command_in_map ("complete", keymap); | 1311 | rl_unbind_command_in_map("complete", keymap); |
| 1188 | rl_unbind_command_in_map ("possible-completions", keymap); | 1312 | rl_unbind_command_in_map("possible-completions", keymap); |
| 1189 | rl_unbind_command_in_map ("insert-completions", keymap); | 1313 | rl_unbind_command_in_map("insert-completions", keymap); |
| 1190 | 1314 | ||
| 1191 | /* bind CTRL-L to clearmsg() */ | 1315 | /* bind CTRL-L to clearmsg() */ |
| 1192 | rl_bind_key ('J'-'@', (rl_command_func_t *) clearpriv); | 1316 | rl_bind_key('J' - '@', (rl_command_func_t *)clearpriv); |
| 1193 | rl_bind_key ('O'-'@', (rl_command_func_t *) clearchan); | 1317 | rl_bind_key('O' - '@', (rl_command_func_t *)clearchan); |
| 1194 | rl_bind_key ('L'-'@', (rl_command_func_t *) forceredraw); | 1318 | rl_bind_key('L' - '@', (rl_command_func_t *)forceredraw); |
| 1195 | rl_bind_key ('B'-'@', (rl_command_func_t *) scrollup); | 1319 | rl_bind_key('B' - '@', (rl_command_func_t *)scrollup); |
| 1196 | rl_bind_key ('P'-'@', (rl_command_func_t *) scrollup); | 1320 | rl_bind_key('P' - '@', (rl_command_func_t *)scrollup); |
| 1197 | rl_bind_key ('F'-'@', (rl_command_func_t *) scrolldown); | 1321 | rl_bind_key('F' - '@', (rl_command_func_t *)scrolldown); |
| 1198 | rl_bind_key ('N'-'@', (rl_command_func_t *) scrolldown); | 1322 | rl_bind_key('N' - '@', (rl_command_func_t *)scrolldown); |
| 1199 | rl_bind_key ('R'-'@', (rl_command_func_t *) scrollwin); | 1323 | rl_bind_key('R' - '@', (rl_command_func_t *)scrollwin); |
| 1200 | rl_bind_key ('T'-'@', (rl_command_func_t *) shrinkprivwin); | 1324 | rl_bind_key('T' - '@', (rl_command_func_t *)shrinkprivwin); |
| 1201 | rl_bind_key ('G'-'@', (rl_command_func_t *) growprivwin); | 1325 | rl_bind_key('G' - '@', (rl_command_func_t *)growprivwin); |
| 1202 | rl_bind_key ('X'-'@', (rl_command_func_t *) toggleprivwin); | 1326 | rl_bind_key('X' - '@', (rl_command_func_t *)toggleprivwin); |
| 1203 | 1327 | ||
| 1204 | rl_generic_bind (ISFUNC, "\\M-[5~", (void *)scrollup, keymap); | 1328 | rl_generic_bind(ISFUNC, "\\M-[5~", (void *)scrollup, keymap); |
| 1205 | rl_generic_bind (ISFUNC, "\\M-[6~", (void *)scrolldown, keymap); | 1329 | rl_generic_bind(ISFUNC, "\\M-[6~", (void *)scrolldown, keymap); |
| 1206 | 1330 | ||
| 1207 | /* bind TAB to menu complete from readline */ | 1331 | /* bind TAB to menu complete from readline */ |
| 1208 | rl_bind_key ('\t', (rl_command_func_t *) rl_menu_complete); | 1332 | rl_bind_key('\t', (rl_command_func_t *)rl_menu_complete); |
| 1209 | 1333 | ||
| 1210 | /* application name for .inputrc - err, we don't load it */ | 1334 | /* application name for .inputrc - err, we don't load it */ |
| 1211 | rl_readline_name = "vchat-client"; | 1335 | rl_readline_name = "vchat-client"; |
| 1212 | 1336 | ||
| 1213 | /* set up nick completion functions .. */ | 1337 | /* set up nick completion functions .. */ |
| 1214 | rl_ignore_completion_duplicates = 0; | 1338 | rl_ignore_completion_duplicates = 0; |
| 1215 | rl_attempted_completion_function = (rl_completion_func_t *) ul_complete_user; | 1339 | rl_attempted_completion_function = (rl_completion_func_t *)ul_complete_user; |
| 1216 | 1340 | ||
| 1217 | /* .. and 'line completed' callback */ | 1341 | /* .. and 'line completed' callback */ |
| 1218 | rl_callback_handler_install ("", (rl_vcpfunc_t *) linecomplete); | 1342 | rl_callback_handler_install("", (rl_vcpfunc_t *)linecomplete); |
| 1219 | 1343 | ||
| 1220 | if( getintoption(CF_PRIVCOLLAPS) ) | 1344 | if (getintoption(CF_PRIVCOLLAPS)) |
| 1221 | toggleprivwin(); | 1345 | toggleprivwin(); |
| 1222 | 1346 | ||
| 1223 | resize(0); | 1347 | resize(0); |
| 1224 | } | 1348 | } |
| 1225 | 1349 | ||
| 1226 | /* render consoleline to screen */ | 1350 | /* render consoleline to screen */ |
| 1227 | void | 1351 | void consoleline(char *message) { |
| 1228 | consoleline (char *message) | ||
| 1229 | { | ||
| 1230 | /* clear console, set string (or default), redraw display */ | 1352 | /* clear console, set string (or default), redraw display */ |
| 1231 | int i; | 1353 | int i; |
| 1232 | ncurs_attr old_att, new_att; | 1354 | ncurs_attr old_att, new_att; |
| 1233 | 1355 | ||
| 1234 | togglequery(); | 1356 | togglequery(); |
| 1235 | 1357 | ||
| 1236 | memset( &new_att, 0, sizeof(new_att)); | 1358 | memset(&new_att, 0, sizeof(new_att)); |
| 1237 | BCOLR_SET( (&new_att), 8 ); | 1359 | BCOLR_SET((&new_att), 8); |
| 1238 | wmove (console, 0, 0); | 1360 | wmove(console, 0, 0); |
| 1239 | WATTR_GET( console, old_att); | 1361 | WATTR_GET(console, old_att); |
| 1240 | if(sb_pub->scroll!=sb_pub->count) WATTR_SET( console, new_att); | 1362 | if (sb_pub->scroll != sb_pub->count) |
| 1363 | WATTR_SET(console, new_att); | ||
| 1241 | 1364 | ||
| 1242 | for (i = 0; i < getmaxx(console) - 1; i++) | 1365 | for (i = 0; i < getmaxx(console) - 1; i++) |
| 1243 | waddch (console, ' '); | 1366 | waddch(console, ' '); |
| 1244 | 1367 | ||
| 1245 | if( !message && usetime ) | 1368 | if (!message && usetime) { |
| 1246 | { | 1369 | char date[10]; |
| 1247 | char date[10]; | 1370 | time_t now = time(NULL); |
| 1248 | time_t now = time(NULL); | 1371 | strftime(date, sizeof(date), getformatstr(FS_CONSOLETIME), localtime(&now)); |
| 1249 | strftime( date, sizeof(date), getformatstr(FS_CONSOLETIME), localtime(&now)); | 1372 | snprintf(tmpstr, TMPSTRSIZE, "%s%s", date, consolestr); |
| 1250 | snprintf( tmpstr, TMPSTRSIZE, "%s%s", date, consolestr); | 1373 | mvwaddnstr(console, 0, 0, tmpstr, getmaxx(console) - 1); |
| 1251 | mvwaddnstr (console, 0, 0, tmpstr, getmaxx(console) - 1); | ||
| 1252 | } else { | 1374 | } else { |
| 1253 | mvwaddnstr (console, 0, 0, message ? message : consolestr, getmaxx(console) - 1); | 1375 | mvwaddnstr(console, 0, 0, message ? message : consolestr, |
| 1376 | getmaxx(console) - 1); | ||
| 1254 | } | 1377 | } |
| 1255 | 1378 | ||
| 1256 | snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_SBINF),sb_pub->scroll,sb_pub->count); | 1379 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_SBINF), sb_pub->scroll, |
| 1257 | mvwaddstr (console, 0, getmaxx(console) - 1 - (strlen(tmpstr)-1),tmpstr); | 1380 | sb_pub->count); |
| 1258 | if (sb_win == 0) mvwaddch (console, 0, getmaxx(console) - 1,'*'); | 1381 | mvwaddstr(console, 0, getmaxx(console) - 1 - (strlen(tmpstr) - 1), tmpstr); |
| 1382 | if (sb_win == 0) | ||
| 1383 | mvwaddch(console, 0, getmaxx(console) - 1, '*'); | ||
| 1259 | 1384 | ||
| 1260 | WATTR_SET( console, old_att); | 1385 | WATTR_SET(console, old_att); |
| 1261 | 1386 | ||
| 1262 | wnoutrefresh(console); | 1387 | wnoutrefresh(console); |
| 1263 | if(outputshown) { | 1388 | if (outputshown) { |
| 1264 | redrawwin(output); | 1389 | redrawwin(output); |
| 1265 | wnoutrefresh(output); | 1390 | wnoutrefresh(output); |
| 1266 | } | 1391 | } |
| 1267 | 1392 | ||
| 1268 | togglequery(); | 1393 | togglequery(); |
| @@ -1271,41 +1396,41 @@ consoleline (char *message) | |||
| 1271 | } | 1396 | } |
| 1272 | 1397 | ||
| 1273 | /* render topicline to screen */ | 1398 | /* render topicline to screen */ |
| 1274 | void | 1399 | void topicline(char *message) { |
| 1275 | topicline (char *message) | ||
| 1276 | { | ||
| 1277 | int i; | 1400 | int i; |
| 1278 | ncurs_attr old_att, new_att; | 1401 | ncurs_attr old_att, new_att; |
| 1279 | 1402 | ||
| 1280 | if( !topic ) | 1403 | if (!topic) |
| 1281 | return; | 1404 | return; |
| 1282 | 1405 | ||
| 1283 | togglequery(); | 1406 | togglequery(); |
| 1284 | 1407 | ||
| 1285 | memset( &new_att, 0, sizeof(new_att)); | 1408 | memset(&new_att, 0, sizeof(new_att)); |
| 1286 | BCOLR_SET( (&new_att), 8 ); | 1409 | BCOLR_SET((&new_att), 8); |
| 1287 | 1410 | ||
| 1288 | /* clear topic, set string (or default), redraw display */ | 1411 | /* clear topic, set string (or default), redraw display */ |
| 1289 | wmove (topic, 0, 0); | 1412 | wmove(topic, 0, 0); |
| 1290 | 1413 | ||
| 1291 | WATTR_GET( topic, old_att); | 1414 | WATTR_GET(topic, old_att); |
| 1292 | if( private && (sb_priv->scroll!=sb_priv->count)) | 1415 | if (private && (sb_priv->scroll != sb_priv->count)) |
| 1293 | WATTR_SET( topic, new_att); | 1416 | WATTR_SET(topic, new_att); |
| 1294 | 1417 | ||
| 1295 | for (i = 0; i < getmaxx(topic) - 1; i++) | 1418 | for (i = 0; i < getmaxx(topic) - 1; i++) |
| 1296 | waddch (topic, ' '); | 1419 | waddch(topic, ' '); |
| 1297 | mvwaddnstr (topic, 0, 0, message ? message : topicstr, getmaxx(topic) - 1); | 1420 | mvwaddnstr(topic, 0, 0, message ? message : topicstr, getmaxx(topic) - 1); |
| 1298 | if (private) { | 1421 | if (private) { |
| 1299 | snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_SBINF),sb_priv->scroll,sb_priv->count); | 1422 | snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_SBINF), sb_priv->scroll, |
| 1300 | mvwaddstr (topic, 0, getmaxx(topic) - 1 - (strlen(tmpstr)-1),tmpstr); | 1423 | sb_priv->count); |
| 1301 | if (sb_win == 1) mvwaddch (topic, 0, getmaxx(topic) - 1,'*'); | 1424 | mvwaddstr(topic, 0, getmaxx(topic) - 1 - (strlen(tmpstr) - 1), tmpstr); |
| 1425 | if (sb_win == 1) | ||
| 1426 | mvwaddch(topic, 0, getmaxx(topic) - 1, '*'); | ||
| 1302 | } | 1427 | } |
| 1303 | WATTR_SET( topic, old_att); | 1428 | WATTR_SET(topic, old_att); |
| 1304 | 1429 | ||
| 1305 | wnoutrefresh(topic); | 1430 | wnoutrefresh(topic); |
| 1306 | if(outputshown) { | 1431 | if (outputshown) { |
| 1307 | redrawwin(output); | 1432 | redrawwin(output); |
| 1308 | wnoutrefresh(output); | 1433 | wnoutrefresh(output); |
| 1309 | } | 1434 | } |
| 1310 | 1435 | ||
| 1311 | togglequery(); | 1436 | togglequery(); |
| @@ -1314,23 +1439,19 @@ topicline (char *message) | |||
| 1314 | } | 1439 | } |
| 1315 | 1440 | ||
| 1316 | /* end userinterface */ | 1441 | /* end userinterface */ |
| 1317 | void | 1442 | void exitui(void) { |
| 1318 | exitui (void) | ||
| 1319 | { | ||
| 1320 | if (ui_init) { | 1443 | if (ui_init) { |
| 1321 | rl_callback_handler_remove (); | 1444 | rl_callback_handler_remove(); |
| 1322 | endwin (); | 1445 | endwin(); |
| 1323 | ui_init = 0; | 1446 | ui_init = 0; |
| 1324 | } | 1447 | } |
| 1325 | } | 1448 | } |
| 1326 | 1449 | ||
| 1327 | /* prompt for a nick */ | 1450 | /* prompt for a nick */ |
| 1328 | /* FIXME: must not be called when used rl_callback_read_char()/userinput() | 1451 | /* FIXME: must not be called when used rl_callback_read_char()/userinput() |
| 1329 | * before */ | 1452 | * before */ |
| 1330 | void | 1453 | void nickprompt(void) { |
| 1331 | nickprompt (void) | 1454 | char *newnick = 0; |
| 1332 | { | ||
| 1333 | char * newnick = 0; | ||
| 1334 | 1455 | ||
| 1335 | if (own_nick_get()) | 1456 | if (own_nick_get()) |
| 1336 | return; | 1457 | return; |
| @@ -1341,15 +1462,15 @@ nickprompt (void) | |||
| 1341 | newnick = readline(""); | 1462 | newnick = readline(""); |
| 1342 | 1463 | ||
| 1343 | own_nick_set(newnick); | 1464 | own_nick_set(newnick); |
| 1344 | setstroption(CF_NICK,newnick); | 1465 | setstroption(CF_NICK, newnick); |
| 1345 | 1466 | ||
| 1346 | /* try to get readlines stats clean again */ | 1467 | /* try to get readlines stats clean again */ |
| 1347 | //rl_free_line_state (); | 1468 | // rl_free_line_state (); |
| 1348 | memset( rl_line_buffer, 0, rl_end ); | 1469 | memset(rl_line_buffer, 0, rl_end); |
| 1349 | rl_point = rl_end = rl_done = 0; | 1470 | rl_point = rl_end = rl_done = 0; |
| 1350 | 1471 | ||
| 1351 | /* wipe input line and reset cursor */ | 1472 | /* wipe input line and reset cursor */ |
| 1352 | rl_kill_full_line(0,0); | 1473 | rl_kill_full_line(0, 0); |
| 1353 | wclear(input); | 1474 | wclear(input); |
| 1354 | 1475 | ||
| 1355 | /* reset consoleline */ | 1476 | /* reset consoleline */ |
| @@ -1357,20 +1478,18 @@ nickprompt (void) | |||
| 1357 | } | 1478 | } |
| 1358 | 1479 | ||
| 1359 | /* special callback for readline, doesn't show the characters */ | 1480 | /* special callback for readline, doesn't show the characters */ |
| 1360 | static void | 1481 | static void vcnredraw(void) { |
| 1361 | vcnredraw (void) | ||
| 1362 | { | ||
| 1363 | int i; | 1482 | int i; |
| 1364 | char *passbof="-*-*-*-*-*-*-"; | 1483 | char *passbof = "-*-*-*-*-*-*-"; |
| 1365 | 1484 | ||
| 1366 | /* wipe input line and reset cursor */ | 1485 | /* wipe input line and reset cursor */ |
| 1367 | wmove(input, 0, 0); | 1486 | wmove(input, 0, 0); |
| 1368 | for (i = 0; i < getmaxx(input) - 1; i++) | 1487 | for (i = 0; i < getmaxx(input) - 1; i++) |
| 1369 | waddch(input, ' '); | 1488 | waddch(input, ' '); |
| 1370 | wmove(input, 0, 0); | 1489 | wmove(input, 0, 0); |
| 1371 | 1490 | ||
| 1372 | /* draw as many stars as there are characters */ | 1491 | /* draw as many stars as there are characters */ |
| 1373 | mvwaddnstr(input, 0, 0, &passbof[rl_point%2], 12); | 1492 | mvwaddnstr(input, 0, 0, &passbof[rl_point % 2], 12); |
| 1374 | wmove(input, 0, getmaxx(input) - 1); | 1493 | wmove(input, 0, getmaxx(input) - 1); |
| 1375 | wrefresh(input); | 1494 | wrefresh(input); |
| 1376 | } | 1495 | } |
| @@ -1378,11 +1497,11 @@ vcnredraw (void) | |||
| 1378 | /* passphrase callback for OpenSSL */ | 1497 | /* passphrase callback for OpenSSL */ |
| 1379 | /* FIXME: must not be called when used rl_callback_read_char()/userinput() | 1498 | /* FIXME: must not be called when used rl_callback_read_char()/userinput() |
| 1380 | * before */ | 1499 | * before */ |
| 1381 | int | 1500 | int passprompt(char *buf, int size, int rwflag, void *userdata) { |
| 1382 | passprompt (char *buf, int size, int rwflag, void *userdata) | ||
| 1383 | { | ||
| 1384 | int i; | 1501 | int i; |
| 1385 | char *passphrase = NULL; | 1502 | char *passphrase = NULL; |
| 1503 | (void)rwflag; | ||
| 1504 | (void)userdata; | ||
| 1386 | 1505 | ||
| 1387 | /* use special non-revealing redraw function */ | 1506 | /* use special non-revealing redraw function */ |
| 1388 | /* FIXME: passphrase isn't protected against e.g. swapping */ | 1507 | /* FIXME: passphrase isn't protected against e.g. swapping */ |
| @@ -1390,45 +1509,43 @@ passprompt (char *buf, int size, int rwflag, void *userdata) | |||
| 1390 | 1509 | ||
| 1391 | /* prompt user for non-empty passphrase */ | 1510 | /* prompt user for non-empty passphrase */ |
| 1392 | consoleline("Please enter PEM passphrase for private key:"); | 1511 | consoleline("Please enter PEM passphrase for private key:"); |
| 1393 | while (!passphrase || !passphrase[0]) | 1512 | while (!passphrase || !passphrase[0]) { |
| 1394 | { | 1513 | if (passphrase) |
| 1395 | if (passphrase) | 1514 | free(passphrase); |
| 1396 | free (passphrase); | 1515 | passphrase = readline(""); |
| 1397 | passphrase = readline (""); | 1516 | } |
| 1398 | } | ||
| 1399 | 1517 | ||
| 1400 | /* reset redrawing function to default, reset consoleline */ | 1518 | /* reset redrawing function to default, reset consoleline */ |
| 1401 | rl_redisplay_function = vciredraw; | 1519 | rl_redisplay_function = vciredraw; |
| 1402 | consoleline(NULL); | 1520 | consoleline(NULL); |
| 1403 | 1521 | ||
| 1404 | /* copy passphrase to buffer */ | 1522 | /* copy passphrase to buffer */ |
| 1405 | strncpy (buf, passphrase, size); | 1523 | strncpy(buf, passphrase, size); |
| 1406 | 1524 | ||
| 1407 | /* try to get readlines stats clean again */ | 1525 | /* try to get readlines stats clean again */ |
| 1408 | //rl_free_line_state (); | 1526 | // rl_free_line_state (); |
| 1409 | memset( rl_line_buffer, 0, rl_end ); | 1527 | memset(rl_line_buffer, 0, rl_end); |
| 1410 | rl_point = rl_end = rl_done = 0; | 1528 | rl_point = rl_end = rl_done = 0; |
| 1411 | 1529 | ||
| 1412 | /* wipe input line and reset cursor */ | 1530 | /* wipe input line and reset cursor */ |
| 1413 | wmove (input, 0, 0); | 1531 | wmove(input, 0, 0); |
| 1414 | for (i = 0; i < getmaxx(input) - 1; i++) | 1532 | for (i = 0; i < getmaxx(input) - 1; i++) |
| 1415 | waddch (input, ' '); | 1533 | waddch(input, ' '); |
| 1416 | wmove (input, 0, 0); | 1534 | wmove(input, 0, 0); |
| 1417 | wrefresh (input); | 1535 | wrefresh(input); |
| 1418 | 1536 | ||
| 1419 | /* return passphrase to OpenSSL */ | 1537 | /* return passphrase to OpenSSL */ |
| 1420 | return strlen (buf); | 1538 | return strlen(buf); |
| 1421 | } | 1539 | } |
| 1422 | 1540 | ||
| 1423 | /* Filter stuff */ | 1541 | /* Filter stuff */ |
| 1424 | static int | 1542 | static int check_valid_colour(char colour) { |
| 1425 | check_valid_colour( char colour ) { | 1543 | return !((colour != '-') && (colour != '+') && |
| 1426 | return !( (colour !='-')&&(colour !='+') && (colour < '0' || colour > '9') && | 1544 | (colour < '0' || colour > '9') && |
| 1427 | (colour < 'A' || colour > 'Z' || !attributes[ colour-'A' ]) && | 1545 | (colour < 'A' || colour > 'Z' || !attributes[colour - 'A']) && |
| 1428 | (colour < 'a' || colour > 'z' || !attributes[ colour-'a' ])); | 1546 | (colour < 'a' || colour > 'z' || !attributes[colour - 'a'])); |
| 1429 | } | 1547 | } |
| 1430 | 1548 | ||
| 1431 | |||
| 1432 | /* scans filterlist and removes possible matches | 1549 | /* scans filterlist and removes possible matches |
| 1433 | test functions may return: | 1550 | test functions may return: |
| 1434 | RMFILTER_RMANDCONT | 1551 | RMFILTER_RMANDCONT |
| @@ -1437,101 +1554,101 @@ check_valid_colour( char colour ) { | |||
| 1437 | RMFILTER_KEEPANDSTOP | 1554 | RMFILTER_KEEPANDSTOP |
| 1438 | returns number of removed entries | 1555 | returns number of removed entries |
| 1439 | */ | 1556 | */ |
| 1440 | static int | 1557 | static int removefromfilterlist(int (*test)(filt *flt, void *data, char colour), |
| 1441 | removefromfilterlist( int(*test)(filt *flt, void *data, char colour), void *data, char colour) { | 1558 | void *data, char colour) { |
| 1442 | filt **flt = &filterlist, *tmp; | 1559 | filt **flt = &filterlist, *tmp; |
| 1443 | int removed = 0, stop = 0; | 1560 | int removed = 0, stop = 0; |
| 1444 | 1561 | ||
| 1445 | while( *flt && !stop ) { | 1562 | while (*flt && !stop) { |
| 1446 | switch( test( *flt, data, colour ) ) { | 1563 | switch (test(*flt, data, colour)) { |
| 1447 | case RMFILTER_RMANDSTOP: /* remove */ | 1564 | case RMFILTER_RMANDSTOP: /* remove */ |
| 1448 | stop = 1; | 1565 | stop = 1; |
| 1449 | case RMFILTER_RMANDCONT: | 1566 | case RMFILTER_RMANDCONT: |
| 1450 | snprintf( tmpstr, TMPSTRSIZE, " Removed ID: [% 3d] Color: [%c] Regex: [%s] ", (*flt)->id, (*flt)->colour, (*flt)->text); | 1567 | snprintf(tmpstr, TMPSTRSIZE, |
| 1451 | writeout(tmpstr); | 1568 | " Removed ID: [% 3d] Color: [%c] Regex: [%s] ", (*flt)->id, |
| 1452 | /* Release regex.h resources */ | 1569 | (*flt)->colour, (*flt)->text); |
| 1453 | regfree( &((*flt)->regex)); | 1570 | writeout(tmpstr); |
| 1454 | /* free ASCII text memory */ | 1571 | /* Release regex.h resources */ |
| 1455 | free( (*flt)->text); | 1572 | regfree(&((*flt)->regex)); |
| 1456 | /* unlink from list */ | 1573 | /* free ASCII text memory */ |
| 1457 | tmp = *flt; | 1574 | free((*flt)->text); |
| 1458 | *flt = (*flt)->next; | 1575 | /* unlink from list */ |
| 1459 | /* free filter block itself */ | 1576 | tmp = *flt; |
| 1460 | free( tmp ); | 1577 | *flt = (*flt)->next; |
| 1461 | /* reflect changes on whole screen */ | 1578 | /* free filter block itself */ |
| 1462 | removed++; | 1579 | free(tmp); |
| 1463 | break; | 1580 | /* reflect changes on whole screen */ |
| 1464 | case RMFILTER_KEEPANDSTOP: /* don't remove but stop scanning */ | 1581 | removed++; |
| 1465 | stop = 1; | 1582 | break; |
| 1466 | break; | 1583 | case RMFILTER_KEEPANDSTOP: /* don't remove but stop scanning */ |
| 1467 | default: | 1584 | stop = 1; |
| 1468 | /* advance in list */ | 1585 | break; |
| 1469 | if( *flt ) flt = &((*flt)->next); | 1586 | default: |
| 1470 | break; | 1587 | /* advance in list */ |
| 1471 | } | 1588 | if (*flt) |
| 1589 | flt = &((*flt)->next); | ||
| 1590 | break; | ||
| 1591 | } | ||
| 1472 | } | 1592 | } |
| 1473 | /* return number of removed items */ | 1593 | /* return number of removed items */ |
| 1474 | return removed; | 1594 | return removed; |
| 1475 | } | 1595 | } |
| 1476 | 1596 | ||
| 1477 | static int | 1597 | static int test_clear(filt *flt, void *data, char c) { |
| 1478 | test_clear( filt *flt, void *data, char c ) { | 1598 | (void)data; |
| 1479 | if( !c || ( c == flt->colour ) || ( (c == '*') && (flt->colour != '-') && (flt->colour != '+') ) ) | 1599 | if (!c || (c == flt->colour) || |
| 1480 | return RMFILTER_RMANDCONT; | 1600 | ((c == '*') && (flt->colour != '-') && (flt->colour != '+'))) |
| 1601 | return RMFILTER_RMANDCONT; | ||
| 1481 | else | 1602 | else |
| 1482 | return RMFILTER_KEEPANDCONT; | 1603 | return RMFILTER_KEEPANDCONT; |
| 1483 | } | 1604 | } |
| 1484 | 1605 | ||
| 1485 | static int | 1606 | static int test_simplerm(filt *flt, void *data, char colour) { |
| 1486 | test_simplerm( filt *flt, void *data, char colour) { | 1607 | if (!strcmp(flt->text, (char *)data)) |
| 1487 | if( !strcmp( flt->text, (char*)data)) | 1608 | return test_clear(flt, NULL, colour); |
| 1488 | return test_clear(flt, NULL, colour); | ||
| 1489 | else | 1609 | else |
| 1490 | return RMFILTER_KEEPANDCONT; | 1610 | return RMFILTER_KEEPANDCONT; |
| 1491 | } | 1611 | } |
| 1492 | 1612 | ||
| 1493 | static int | 1613 | static int test_numericrm(filt *flt, void *data, char colour) { |
| 1494 | test_numericrm( filt *flt, void *data, char colour) { | 1614 | if (flt->id == (long)data) |
| 1495 | if( flt->id == (long)data) | 1615 | return test_clear(flt, NULL, colour); |
| 1496 | return test_clear(flt, NULL, colour); | ||
| 1497 | else | 1616 | else |
| 1498 | return RMFILTER_KEEPANDCONT; | 1617 | return RMFILTER_KEEPANDCONT; |
| 1499 | } | 1618 | } |
| 1500 | 1619 | ||
| 1501 | /* clears filter list */ | 1620 | /* clears filter list */ |
| 1502 | void | 1621 | void clearfilters(char colour) { |
| 1503 | clearfilters( char colour ) { | 1622 | flushout(); |
| 1504 | flushout( ); | 1623 | if (removefromfilterlist(test_clear, NULL, colour)) { |
| 1505 | if( removefromfilterlist( test_clear, NULL, colour ) ) { | 1624 | /* There actually WERE items removed */ |
| 1506 | /* There actually WERE items removed */ | 1625 | filtertype = analyzefilters(); |
| 1507 | filtertype = analyzefilters( ); | ||
| 1508 | } else { | 1626 | } else { |
| 1509 | writeout(" No matches on filter list. "); | 1627 | writeout(" No matches on filter list. "); |
| 1510 | } | 1628 | } |
| 1511 | showout(); | 1629 | showout(); |
| 1512 | } | 1630 | } |
| 1513 | 1631 | ||
| 1514 | /* removes filter pattern */ | 1632 | /* removes filter pattern */ |
| 1515 | void | 1633 | void removefilter(char *tail) { |
| 1516 | removefilter( char *tail ) { | ||
| 1517 | int rmv = 0, val; | 1634 | int rmv = 0, val; |
| 1518 | char* end; | 1635 | char *end; |
| 1519 | 1636 | ||
| 1520 | flushout( ); | 1637 | flushout(); |
| 1521 | 1638 | ||
| 1522 | rmv = removefromfilterlist( test_simplerm, (void *)tail, 0 ); | 1639 | rmv = removefromfilterlist(test_simplerm, (void *)tail, 0); |
| 1523 | if(!rmv) { | 1640 | if (!rmv) { |
| 1524 | val = strtol(tail, &end, 10); | 1641 | val = strtol(tail, &end, 10); |
| 1525 | if( (tail != end) && (!*end) ) | 1642 | if ((tail != end) && (!*end)) |
| 1526 | rmv = removefromfilterlist( test_numericrm, (void *)(uintptr_t)val, 0); | 1643 | rmv = removefromfilterlist(test_numericrm, (void *)(uintptr_t)val, 0); |
| 1527 | } | 1644 | } |
| 1528 | 1645 | ||
| 1529 | if( rmv ) { | 1646 | if (rmv) { |
| 1530 | /* There actually WERE items removed */ | 1647 | /* There actually WERE items removed */ |
| 1531 | filtertype = analyzefilters( ); | 1648 | filtertype = analyzefilters(); |
| 1532 | } else { | 1649 | } else { |
| 1533 | snprintf( tmpstr, TMPSTRSIZE, " Not on filter list: %s ", tail); | 1650 | snprintf(tmpstr, TMPSTRSIZE, " Not on filter list: %s ", tail); |
| 1534 | writeout( tmpstr ); | 1651 | writeout(tmpstr); |
| 1535 | } | 1652 | } |
| 1536 | showout(); | 1653 | showout(); |
| 1537 | } | 1654 | } |
| @@ -1539,139 +1656,158 @@ removefilter( char *tail ) { | |||
| 1539 | static unsigned int uniqueidpool = 1; | 1656 | static unsigned int uniqueidpool = 1; |
| 1540 | 1657 | ||
| 1541 | /* returns unique id for filter pattern or 0 for failure */ | 1658 | /* returns unique id for filter pattern or 0 for failure */ |
| 1542 | unsigned int | 1659 | unsigned int addfilter(char colour, char *regex) { |
| 1543 | addfilter( char colour, char *regex ) { | 1660 | filt *newflt = malloc(sizeof(filt)), **flt = &filterlist; |
| 1544 | filt *newflt = malloc( sizeof(filt)), **flt = &filterlist; | ||
| 1545 | 1661 | ||
| 1546 | if( !newflt ) return 0; | 1662 | if (!newflt) |
| 1547 | flushout( ); | 1663 | return 0; |
| 1664 | flushout(); | ||
| 1548 | 1665 | ||
| 1549 | /* check colour validity */ | 1666 | /* check colour validity */ |
| 1550 | if( !check_valid_colour( colour ) ){ | 1667 | if (!check_valid_colour(colour)) { |
| 1551 | free( newflt ); | 1668 | free(newflt); |
| 1552 | writeout( " Not a valid colour code. " ); | 1669 | writeout(" Not a valid colour code. "); |
| 1553 | showout( ); | 1670 | showout(); |
| 1554 | return 0; | 1671 | return 0; |
| 1555 | } | 1672 | } |
| 1556 | 1673 | ||
| 1557 | if( regcomp( &newflt->regex, regex, REG_ICASE | REG_EXTENDED | REG_NEWLINE) ) { | 1674 | if (regcomp(&newflt->regex, regex, REG_ICASE | REG_EXTENDED | REG_NEWLINE)) { |
| 1558 | /* couldn't compile regex ... print error, return */ | 1675 | /* couldn't compile regex ... print error, return */ |
| 1559 | free( newflt ); | 1676 | free(newflt); |
| 1560 | 1677 | ||
| 1561 | snprintf( tmpstr, TMPSTRSIZE, " %s ", regex); | 1678 | snprintf(tmpstr, TMPSTRSIZE, " %s ", regex); |
| 1562 | writeout( " Bad regular expression: "); | 1679 | writeout(" Bad regular expression: "); |
| 1563 | writeout( tmpstr ); | 1680 | writeout(tmpstr); |
| 1564 | showout( ); | 1681 | showout(); |
| 1565 | return 0; | 1682 | return 0; |
| 1566 | } else { | 1683 | } else { |
| 1567 | int len = strlen(regex) + 1; | 1684 | int len = strlen(regex) + 1; |
| 1568 | /* grab id from ID pool an increase free ID counter */ | 1685 | /* grab id from ID pool an increase free ID counter */ |
| 1569 | newflt->id = uniqueidpool++; | 1686 | newflt->id = uniqueidpool++; |
| 1570 | newflt->colour = colour; | 1687 | newflt->colour = colour; |
| 1571 | newflt->next = NULL; | 1688 | newflt->next = NULL; |
| 1572 | /* take a copy of plain regex text for later identification by user */ | 1689 | /* take a copy of plain regex text for later identification by user */ |
| 1573 | newflt->text = malloc( len ); | 1690 | newflt->text = malloc(len); |
| 1574 | memcpy( newflt->text, regex, len ); | 1691 | memcpy(newflt->text, regex, len); |
| 1575 | } | 1692 | } |
| 1576 | 1693 | ||
| 1577 | /* append new filter to filterlist */ | 1694 | /* append new filter to filterlist */ |
| 1578 | while( *flt ) flt=&((*flt)->next); | 1695 | while (*flt) |
| 1696 | flt = &((*flt)->next); | ||
| 1579 | *flt = newflt; | 1697 | *flt = newflt; |
| 1580 | 1698 | ||
| 1581 | filtertype = analyzefilters( ); | 1699 | filtertype = analyzefilters(); |
| 1582 | 1700 | ||
| 1583 | if ( colour == '-' ) { | 1701 | if (colour == '-') { |
| 1584 | snprintf( tmpstr, TMPSTRSIZE, " \"%s\" successfully added to ignorance list. ( ID = %d). ", (*flt)->text, (*flt)->id); | 1702 | snprintf(tmpstr, TMPSTRSIZE, |
| 1585 | } else if( colour == '+' ) { | 1703 | " \"%s\" successfully added to ignorance list. ( ID = %d). ", |
| 1586 | snprintf( tmpstr, TMPSTRSIZE, " \"%s\" successfully added to zoom list. ( ID = %d). ", (*flt)->text, (*flt)->id); | 1704 | (*flt)->text, (*flt)->id); |
| 1705 | } else if (colour == '+') { | ||
| 1706 | snprintf(tmpstr, TMPSTRSIZE, | ||
| 1707 | " \"%s\" successfully added to zoom list. ( ID = %d). ", | ||
| 1708 | (*flt)->text, (*flt)->id); | ||
| 1587 | } else { | 1709 | } else { |
| 1588 | snprintf( tmpstr, TMPSTRSIZE, " \"%s\" successfully added to hilitelist. (ID = %d). ", (*flt)->text, (*flt)->id); | 1710 | snprintf(tmpstr, TMPSTRSIZE, |
| 1711 | " \"%s\" successfully added to hilitelist. (ID = %d). ", | ||
| 1712 | (*flt)->text, (*flt)->id); | ||
| 1589 | } | 1713 | } |
| 1590 | writeout(tmpstr ); | 1714 | writeout(tmpstr); |
| 1591 | showout( ); | 1715 | showout(); |
| 1592 | 1716 | ||
| 1593 | return newflt->id; | 1717 | return newflt->id; |
| 1594 | } | 1718 | } |
| 1595 | 1719 | ||
| 1596 | void | 1720 | void listfilters(void) { |
| 1597 | listfilters( void ) { | 1721 | filt *flt = filterlist; |
| 1598 | filt *flt = filterlist; | 1722 | int shownhi = 0, shownign = 0, shownzoom = 0; |
| 1599 | int shownhi = 0, shownign = 0, shownzoom = 0; | ||
| 1600 | 1723 | ||
| 1601 | flushout( ); | 1724 | flushout(); |
| 1602 | 1725 | ||
| 1603 | while( flt ) { | 1726 | while (flt) { |
| 1604 | if( (flt->colour != '-') && (flt->colour != '+')) { | 1727 | if ((flt->colour != '-') && (flt->colour != '+')) { |
| 1605 | if(!shownhi) { | 1728 | if (!shownhi) { |
| 1606 | writeout(" Your hilites:"); | 1729 | writeout(" Your hilites:"); |
| 1607 | shownhi = 1; | 1730 | shownhi = 1; |
| 1608 | } | ||
| 1609 | snprintf( tmpstr, TMPSTRSIZE, " ID: [% 3d] Color: [%c] Regex: [%s]", flt->id, flt->colour, flt->text); | ||
| 1610 | writeout( tmpstr ); | ||
| 1611 | } | 1731 | } |
| 1612 | flt = flt->next; | 1732 | snprintf(tmpstr, TMPSTRSIZE, " ID: [% 3d] Color: [%c] Regex: [%s]", |
| 1733 | flt->id, flt->colour, flt->text); | ||
| 1734 | writeout(tmpstr); | ||
| 1735 | } | ||
| 1736 | flt = flt->next; | ||
| 1613 | } | 1737 | } |
| 1614 | 1738 | ||
| 1615 | flt = filterlist; | 1739 | flt = filterlist; |
| 1616 | 1740 | ||
| 1617 | while( flt ) { | 1741 | while (flt) { |
| 1618 | if( flt->colour == '-') { | 1742 | if (flt->colour == '-') { |
| 1619 | if(!shownign) { | 1743 | if (!shownign) { |
| 1620 | if(shownhi) writeout(" "); | 1744 | if (shownhi) |
| 1621 | writeout(" You do ignore:"); | 1745 | writeout(" "); |
| 1622 | shownign = 1; | 1746 | writeout(" You do ignore:"); |
| 1623 | } | 1747 | shownign = 1; |
| 1624 | snprintf( tmpstr, TMPSTRSIZE, " ID: [% 3d] Regex: [%s]", flt->id, flt->text); | ||
| 1625 | writeout( tmpstr ); | ||
| 1626 | } | 1748 | } |
| 1627 | flt = flt->next; | 1749 | snprintf(tmpstr, TMPSTRSIZE, " ID: [% 3d] Regex: [%s]", |
| 1750 | flt->id, flt->text); | ||
| 1751 | writeout(tmpstr); | ||
| 1752 | } | ||
| 1753 | flt = flt->next; | ||
| 1628 | } | 1754 | } |
| 1629 | 1755 | ||
| 1630 | flt = filterlist; | 1756 | flt = filterlist; |
| 1631 | 1757 | ||
| 1632 | while( flt ) { | 1758 | while (flt) { |
| 1633 | if( flt->colour == '+') { | 1759 | if (flt->colour == '+') { |
| 1634 | if(!shownzoom) { | 1760 | if (!shownzoom) { |
| 1635 | if(shownhi || shownign) writeout(" "); | 1761 | if (shownhi || shownign) |
| 1636 | writeout(" On your whitelist:"); | 1762 | writeout(" "); |
| 1637 | shownzoom = 1; | 1763 | writeout(" On your whitelist:"); |
| 1638 | } | 1764 | shownzoom = 1; |
| 1639 | snprintf( tmpstr, TMPSTRSIZE, " ID: [% 3d] Regex: [%s]", flt->id, flt->text); | ||
| 1640 | writeout( tmpstr ); | ||
| 1641 | } | 1765 | } |
| 1642 | flt = flt->next; | 1766 | snprintf(tmpstr, TMPSTRSIZE, " ID: [% 3d] Regex: [%s]", |
| 1767 | flt->id, flt->text); | ||
| 1768 | writeout(tmpstr); | ||
| 1769 | } | ||
| 1770 | flt = flt->next; | ||
| 1643 | } | 1771 | } |
| 1644 | 1772 | ||
| 1645 | if( !shownign && !shownhi && !shownzoom) { | 1773 | if (!shownign && !shownhi && !shownzoom) { |
| 1646 | writeout(" No entries on your filter list. "); | 1774 | writeout(" No entries on your filter list. "); |
| 1647 | } | 1775 | } |
| 1648 | showout(); | 1776 | showout(); |
| 1649 | } | 1777 | } |
| 1650 | 1778 | ||
| 1651 | void | 1779 | void handlequery(char *tail) { |
| 1652 | handlequery( char *tail ) { | 1780 | if (*tail) { |
| 1653 | if( *tail ) { | ||
| 1654 | // ".m %s " -> string + 4 | 1781 | // ".m %s " -> string + 4 |
| 1655 | if( querypartner && private ) { | 1782 | if (querypartner && private) { |
| 1656 | WINDOW *tmp= private; private = channel; channel = tmp; | 1783 | WINDOW *tmp = private; |
| 1784 | private | ||
| 1785 | = channel; | ||
| 1786 | channel = tmp; | ||
| 1657 | } | 1787 | } |
| 1658 | querypartner = (char *)realloc( querypartner, 5 + strlen( tail )); | 1788 | querypartner = (char *)realloc(querypartner, 5 + strlen(tail)); |
| 1659 | if( querypartner ) { | 1789 | if (querypartner) { |
| 1660 | snprintf( querypartner, 5 + strlen( tail ), ".m %s ", tail ); | 1790 | snprintf(querypartner, 5 + strlen(tail), ".m %s ", tail); |
| 1661 | if( private ) { | 1791 | if (private) { |
| 1662 | WINDOW *tmp= private; private = channel; channel = tmp; | 1792 | WINDOW *tmp = private; |
| 1793 | private | ||
| 1794 | = channel; | ||
| 1795 | channel = tmp; | ||
| 1663 | } | 1796 | } |
| 1664 | } | 1797 | } |
| 1665 | resize( 0 ); | 1798 | resize(0); |
| 1666 | } else { | 1799 | } else { |
| 1667 | // QUERY ends | 1800 | // QUERY ends |
| 1668 | if( querypartner ) { | 1801 | if (querypartner) { |
| 1669 | free( querypartner ); | 1802 | free(querypartner); |
| 1670 | querypartner = NULL; | 1803 | querypartner = NULL; |
| 1671 | if( private ) { | 1804 | if (private) { |
| 1672 | WINDOW *tmp= private; private = channel; channel = tmp; | 1805 | WINDOW *tmp = private; |
| 1806 | private | ||
| 1807 | = channel; | ||
| 1808 | channel = tmp; | ||
| 1673 | } | 1809 | } |
| 1674 | resize( 0 ); | 1810 | resize(0); |
| 1675 | } | 1811 | } |
| 1676 | } | 1812 | } |
| 1677 | } | 1813 | } |
diff --git a/vchat-user.c b/vchat-user.c index 881a3cf..1ab2048 100755..100644 --- a/vchat-user.c +++ b/vchat-user.c | |||
| @@ -3,42 +3,48 @@ | |||
| 3 | 3 | ||
| 4 | */ | 4 | */ |
| 5 | 5 | ||
| 6 | #include <regex.h> | ||
| 6 | #include <stdint.h> | 7 | #include <stdint.h> |
| 8 | #include <stdio.h> | ||
| 7 | #include <stdlib.h> | 9 | #include <stdlib.h> |
| 8 | #include <strings.h> | 10 | #include <strings.h> |
| 9 | #include <stdio.h> | ||
| 10 | #include <sys/time.h> | 11 | #include <sys/time.h> |
| 11 | #include <regex.h> | 12 | |
| 12 | #include <readline/readline.h> | 13 | #include <readline/readline.h> |
| 13 | 14 | ||
| 14 | #include "vchat.h" | ||
| 15 | #include "vchat-user.h" | 15 | #include "vchat-user.h" |
| 16 | #include "vchat.h" | ||
| 16 | 17 | ||
| 17 | /* version of this module */ | 18 | /* version of this module */ |
| 18 | char *vchat_us_version = "vchat-user.c $Id$"; | 19 | char *vchat_us_version = |
| 19 | 20 | "vchat-user.c $Id$"; | |
| 20 | typedef struct | 21 | |
| 21 | { | 22 | typedef struct { |
| 22 | char *nick; | 23 | char *nick; |
| 23 | enum { UL_NONE = 0x00, UL_ME = 0x01, UL_IN_MY_CHAN = 0x02, UL_NOT_IN_LIST = 0x04 } flags; | 24 | enum { |
| 25 | UL_NONE = 0x00, | ||
| 26 | UL_ME = 0x01, | ||
| 27 | UL_IN_MY_CHAN = 0x02, | ||
| 28 | UL_NOT_IN_LIST = 0x04 | ||
| 29 | } flags; | ||
| 24 | uint64_t last_public; | 30 | uint64_t last_public; |
| 25 | uint64_t last_private; | 31 | uint64_t last_private; |
| 26 | } user; | 32 | } user; |
| 27 | static user *g_users; //< all users, incl self | 33 | static user *g_users; //< all users, incl self |
| 28 | static size_t g_users_count; //< number of users in list | 34 | static size_t g_users_count; //< number of users in list |
| 29 | static char *g_nick; //< own nick | 35 | static char *g_nick; //< own nick |
| 30 | static int g_channel; //< own channel | 36 | static int g_channel; //< own channel |
| 31 | unsigned int ul_case_first = 0; | 37 | unsigned int ul_case_first = 0; |
| 32 | 38 | ||
| 33 | static char **g_dict; | 39 | static char **g_dict; |
| 34 | static size_t g_dict_len; | 40 | static size_t g_dict_len; |
| 35 | 41 | ||
| 36 | static int ul_nick_lookup( const char *nick, int *exact_match ) { | 42 | static int ul_nick_lookup(const char *nick, int *exact_match) { |
| 37 | int i; | 43 | int i; |
| 38 | 44 | ||
| 39 | *exact_match = 1; | 45 | *exact_match = 1; |
| 40 | for( i=0; i<g_users_count; ++i ) | 46 | for (i = 0; i < g_users_count; ++i) |
| 41 | if( !strcasecmp( g_users[i].nick, nick ) ) | 47 | if (!strcasecmp(g_users[i].nick, nick)) |
| 42 | return i; | 48 | return i; |
| 43 | *exact_match = 0; | 49 | *exact_match = 0; |
| 44 | return i; | 50 | return i; |
| @@ -46,78 +52,83 @@ static int ul_nick_lookup( const char *nick, int *exact_match ) { | |||
| 46 | 52 | ||
| 47 | static int64_t ul_now() { | 53 | static int64_t ul_now() { |
| 48 | struct timeval now; | 54 | struct timeval now; |
| 49 | gettimeofday(&now,(struct timezone*) 0); | 55 | gettimeofday(&now, (struct timezone *)0); |
| 50 | return ((uint64_t)now.tv_sec * 1000) + ((uint64_t)now.tv_usec / 1000 ); | 56 | return ((uint64_t)now.tv_sec * 1000) + ((uint64_t)now.tv_usec / 1000); |
| 51 | } | 57 | } |
| 52 | 58 | ||
| 53 | /* own nick and channel setters/getters */ | 59 | /* own nick and channel setters/getters */ |
| 54 | void own_nick_set( char *nick ) { | 60 | void own_nick_set(char *nick) { |
| 55 | if( nick ) { | 61 | if (nick) { |
| 56 | int base; | 62 | int base; |
| 57 | if( g_nick ) | 63 | if (g_nick) |
| 58 | base = ul_rename( g_nick, nick ); | 64 | base = ul_rename(g_nick, nick); |
| 59 | else | 65 | else |
| 60 | base = ul_add( nick, 0 ); | 66 | base = ul_add(nick, 0); |
| 61 | if( base >= 0 ) { | 67 | if (base >= 0) { |
| 62 | g_users[base].flags |= UL_ME; | 68 | g_users[base].flags |= UL_ME; |
| 63 | g_nick = g_users[base].nick; | 69 | g_nick = g_users[base].nick; |
| 64 | } | 70 | } |
| 65 | } else | 71 | } else |
| 66 | ul_del( g_nick ); | 72 | ul_del(g_nick); |
| 67 | 73 | ||
| 68 | setstroption(CF_NICK, nick); | 74 | setstroption(CF_NICK, nick); |
| 69 | } | 75 | } |
| 70 | 76 | ||
| 71 | void own_channel_set( int channel ) { | 77 | void own_channel_set(int channel) { |
| 72 | if( channel != g_channel ) { | 78 | if (channel != g_channel) { |
| 73 | /* Remove all users from my chan, will be re-set on join message */ | 79 | /* Remove all users from my chan, will be re-set on join message */ |
| 74 | int i; | 80 | int i; |
| 75 | for( i=0; i<g_users_count; ++i ) | 81 | for (i = 0; i < g_users_count; ++i) |
| 76 | g_users[i].flags &= ~UL_IN_MY_CHAN; | 82 | g_users[i].flags &= ~UL_IN_MY_CHAN; |
| 77 | } | 83 | } |
| 78 | 84 | ||
| 79 | g_channel = channel; | 85 | g_channel = channel; |
| 80 | } | 86 | } |
| 81 | 87 | ||
| 82 | char const *own_nick_get( ) { | 88 | char const *own_nick_get() { return g_nick; } |
| 83 | return g_nick; | ||
| 84 | } | ||
| 85 | 89 | ||
| 86 | int own_nick_check( char *nick ) { | 90 | int own_nick_check(char *nick) { |
| 87 | if( !g_nick ) return -1; | 91 | if (!g_nick) |
| 88 | return !strcasecmp(g_nick,nick); | 92 | return -1; |
| 93 | return !strcasecmp(g_nick, nick); | ||
| 89 | } | 94 | } |
| 90 | 95 | ||
| 91 | int own_channel_get( ) { | 96 | int own_channel_get() { return g_channel; } |
| 92 | return g_channel; | ||
| 93 | } | ||
| 94 | 97 | ||
| 95 | /* Add/remove/rename */ | 98 | /* Add/remove/rename */ |
| 96 | int ul_add(char *name, int in_my_chan_flag ) { | 99 | int ul_add(char *name, int in_my_chan_flag) { |
| 97 | 100 | ||
| 98 | /* Test if user is already known */ | 101 | /* Test if user is already known */ |
| 99 | int exact_match, base = ul_nick_lookup( name, &exact_match ); | 102 | int exact_match, base = ul_nick_lookup(name, &exact_match); |
| 100 | if( !exact_match ) { | 103 | if (!exact_match) { |
| 101 | /* Make space for new user */ | 104 | /* Make space for new user */ |
| 102 | user * new_users = realloc( g_users, sizeof( user ) * ( 1 + g_users_count ) ); | 105 | user *new_users = realloc(g_users, sizeof(user) * (1 + g_users_count)); |
| 103 | if( !new_users ) return -1; | 106 | if (!new_users) |
| 107 | return -1; | ||
| 104 | 108 | ||
| 105 | /* Copy the tail */ | 109 | /* Copy the tail */ |
| 106 | g_users = new_users; | 110 | g_users = new_users; |
| 107 | memmove( g_users + base + 1, g_users + base, ( g_users_count - base ) * sizeof( user ) ); | 111 | memmove(g_users + base + 1, g_users + base, |
| 108 | g_users[base].nick = strdup( name ); | 112 | (g_users_count - base) * sizeof(user)); |
| 109 | g_users[base].flags = UL_NONE; | 113 | g_users[base].nick = strdup(name); |
| 110 | g_users[base].last_public = 0; | 114 | g_users[base].flags = UL_NONE; |
| 115 | g_users[base].last_public = 0; | ||
| 111 | g_users[base].last_private = 0; | 116 | g_users[base].last_private = 0; |
| 112 | 117 | ||
| 113 | g_users_count++; | 118 | g_users_count++; |
| 114 | } | 119 | } |
| 115 | 120 | ||
| 116 | g_users[base].flags &= ~UL_NOT_IN_LIST; | 121 | g_users[base].flags &= ~UL_NOT_IN_LIST; |
| 117 | switch( in_my_chan_flag ) { | 122 | switch (in_my_chan_flag) { |
| 118 | case 1: g_users[base].flags |= UL_IN_MY_CHAN; break; | 123 | case 1: |
| 119 | case 0: g_users[base].flags &= ~UL_IN_MY_CHAN; break; | 124 | g_users[base].flags |= UL_IN_MY_CHAN; |
| 120 | case -1: default: break; | 125 | break; |
| 126 | case 0: | ||
| 127 | g_users[base].flags &= ~UL_IN_MY_CHAN; | ||
| 128 | break; | ||
| 129 | case -1: | ||
| 130 | default: | ||
| 131 | break; | ||
| 121 | } | 132 | } |
| 122 | 133 | ||
| 123 | return base; | 134 | return base; |
| @@ -125,30 +136,33 @@ int ul_add(char *name, int in_my_chan_flag ) { | |||
| 125 | 136 | ||
| 126 | int ul_del(char *name) { | 137 | int ul_del(char *name) { |
| 127 | /* Test if user is already known */ | 138 | /* Test if user is already known */ |
| 128 | int exact_match, base = ul_nick_lookup( name, &exact_match ); | 139 | int exact_match, base = ul_nick_lookup(name, &exact_match); |
| 129 | if( !exact_match ) return -1; | 140 | if (!exact_match) |
| 141 | return -1; | ||
| 130 | 142 | ||
| 131 | /* Release the name buffer */ | 143 | /* Release the name buffer */ |
| 132 | free( g_users[base].nick ); | 144 | free(g_users[base].nick); |
| 133 | if( g_users[base].flags & UL_ME ) g_nick = 0; | 145 | if (g_users[base].flags & UL_ME) |
| 146 | g_nick = 0; | ||
| 134 | 147 | ||
| 135 | /* Copy the tail */ | 148 | /* Copy the tail */ |
| 136 | memmove( g_users + base, g_users + base + 1, ( g_users_count - base - 1 ) * sizeof( user ) ); | 149 | memmove(g_users + base, g_users + base + 1, |
| 150 | (g_users_count - base - 1) * sizeof(user)); | ||
| 137 | 151 | ||
| 138 | /* Shrink user list, realloc to a smaller size never fails */ | 152 | /* Shrink user list, realloc to a smaller size never fails */ |
| 139 | g_users = realloc( g_users, sizeof( user ) * --g_users_count ); | 153 | g_users = realloc(g_users, sizeof(user) * --g_users_count); |
| 140 | return 0; | 154 | return 0; |
| 141 | } | 155 | } |
| 142 | 156 | ||
| 143 | int ul_rename(char *oldname, char *newname) { | 157 | int ul_rename(char *oldname, char *newname) { |
| 144 | /* Ensure user */ | 158 | /* Ensure user */ |
| 145 | int base = ul_add( oldname, -1 ); | 159 | int base = ul_add(oldname, -1); |
| 146 | if( base >= 0 ) { | 160 | if (base >= 0) { |
| 147 | free( g_users[base].nick ); | 161 | free(g_users[base].nick); |
| 148 | g_users[base].nick = strdup( newname ); | 162 | g_users[base].nick = strdup(newname); |
| 149 | if( g_users[base].flags & UL_ME ) | 163 | if (g_users[base].flags & UL_ME) |
| 150 | g_nick = g_users[base].nick; | 164 | g_nick = g_users[base].nick; |
| 151 | if( g_users[base].flags & UL_IN_MY_CHAN ) | 165 | if (g_users[base].flags & UL_IN_MY_CHAN) |
| 152 | ul_public_action(newname); | 166 | ul_public_action(newname); |
| 153 | } | 167 | } |
| 154 | return base; | 168 | return base; |
| @@ -156,23 +170,23 @@ int ul_rename(char *oldname, char *newname) { | |||
| 156 | 170 | ||
| 157 | void ul_clear() { | 171 | void ul_clear() { |
| 158 | int i; | 172 | int i; |
| 159 | for( i=0; i<g_users_count; ++i ) | 173 | for (i = 0; i < g_users_count; ++i) |
| 160 | free( g_users[i].nick ); | 174 | free(g_users[i].nick); |
| 161 | free( g_users ); | 175 | free(g_users); |
| 162 | g_nick = 0; | 176 | g_nick = 0; |
| 163 | } | 177 | } |
| 164 | 178 | ||
| 165 | void ul_rebuild_list( ) { | 179 | void ul_rebuild_list() { |
| 166 | int i; | 180 | int i; |
| 167 | for( i=0; i<g_users_count; ++i ) | 181 | for (i = 0; i < g_users_count; ++i) |
| 168 | g_users[i].flags |= UL_NOT_IN_LIST; | 182 | g_users[i].flags |= UL_NOT_IN_LIST; |
| 169 | } | 183 | } |
| 170 | 184 | ||
| 171 | void ul_clean() { | 185 | void ul_clean() { |
| 172 | int i; | 186 | int i; |
| 173 | for( i=0; i<g_users_count; ++i ) { | 187 | for (i = 0; i < g_users_count; ++i) { |
| 174 | if( g_users[i].flags & UL_NOT_IN_LIST ) { | 188 | if (g_users[i].flags & UL_NOT_IN_LIST) { |
| 175 | ul_del( g_users[i].nick ); | 189 | ul_del(g_users[i].nick); |
| 176 | --i; | 190 | --i; |
| 177 | } | 191 | } |
| 178 | } | 192 | } |
| @@ -187,237 +201,270 @@ void ul_leave_chan(char *name) { | |||
| 187 | void ul_enter_chan(char *name) { | 201 | void ul_enter_chan(char *name) { |
| 188 | /* Ensure user and put him on the channel */ | 202 | /* Ensure user and put him on the channel */ |
| 189 | int base = ul_add(name, 1); | 203 | int base = ul_add(name, 1); |
| 190 | if( base >= 0 ) | 204 | if (base >= 0) |
| 191 | ul_public_action(name); | 205 | ul_public_action(name); |
| 192 | 206 | ||
| 193 | /* Reflect in UI */ | 207 | /* Reflect in UI */ |
| 194 | if( own_nick_check( name ) ) | 208 | if (own_nick_check(name)) |
| 195 | ownjoin( g_channel ); | 209 | ownjoin(g_channel); |
| 196 | } | 210 | } |
| 197 | 211 | ||
| 198 | void ul_private_action(char *name) { | 212 | void ul_private_action(char *name) { |
| 199 | /* Ensure user and keep channel state */ | 213 | /* Ensure user and keep channel state */ |
| 200 | int base = ul_add(name, -1); | 214 | int base = ul_add(name, -1); |
| 201 | if( base >= 0 ) | 215 | if (base >= 0) |
| 202 | g_users[base].last_private = ul_now(); | 216 | g_users[base].last_private = ul_now(); |
| 203 | } | 217 | } |
| 204 | 218 | ||
| 205 | void ul_public_action(char *name) { | 219 | void ul_public_action(char *name) { |
| 206 | /* Ensure user and put him on the channel */ | 220 | /* Ensure user and put him on the channel */ |
| 207 | int base = ul_add(name, 1); | 221 | int base = ul_add(name, 1); |
| 208 | if( base >= 0 ) | 222 | if (base >= 0) |
| 209 | g_users[base].last_public = ul_now(); | 223 | g_users[base].last_public = ul_now(); |
| 210 | } | 224 | } |
| 211 | 225 | ||
| 212 | void ul_add_to_dict(char *dict_items) { | 226 | void ul_add_to_dict(char *dict_items) { |
| 213 | char *i; | 227 | char *i; |
| 214 | for(i=strtok(dict_items," ");i;i=strtok(0," ")) { | 228 | for (i = strtok(dict_items, " "); i; i = strtok(0, " ")) { |
| 215 | g_dict = realloc( g_dict, sizeof(char*) * ( 1 + g_dict_len ) ); | 229 | g_dict = realloc(g_dict, sizeof(char *) * (1 + g_dict_len)); |
| 216 | if( !g_dict ) exit(1); | 230 | if (!g_dict) |
| 231 | exit(1); | ||
| 217 | g_dict[g_dict_len++] = strdup(i); | 232 | g_dict[g_dict_len++] = strdup(i); |
| 218 | } | 233 | } |
| 219 | } | 234 | } |
| 220 | 235 | ||
| 221 | /* Finding users ul_finduser? */ | 236 | /* Finding users ul_finduser? */ |
| 222 | char * ul_match_user(char *regex) { | 237 | char *ul_match_user(char *regex) { |
| 223 | char *dest = tmpstr; | 238 | char *dest = tmpstr; |
| 224 | int i; | 239 | int i; |
| 225 | regex_t preg; | 240 | regex_t preg; |
| 226 | 241 | ||
| 227 | *dest = 0; | 242 | *dest = 0; |
| 228 | if( !regcomp( &preg, regex, REG_ICASE | REG_EXTENDED | REG_NEWLINE)) { | 243 | if (!regcomp(&preg, regex, REG_ICASE | REG_EXTENDED | REG_NEWLINE)) { |
| 229 | 244 | ||
| 230 | /* does the username match? */ | 245 | /* does the username match? */ |
| 231 | /* XXX overflow for too many matches */ | 246 | /* XXX overflow for too many matches */ |
| 232 | for( i=0; i<g_users_count; ++i ) | 247 | for (i = 0; i < g_users_count; ++i) |
| 233 | if( !regexec( &preg, g_users[i].nick, 0, NULL, 0)) /* append username to list */ | 248 | if (!regexec(&preg, g_users[i].nick, 0, NULL, |
| 234 | dest += snprintf ( dest, 256, " %s", g_users[i].nick); | 249 | 0)) /* append username to list */ |
| 235 | 250 | dest += snprintf(dest, 256, " %s", g_users[i].nick); | |
| 236 | } | 251 | } |
| 237 | regfree( &preg ); | 252 | regfree(&preg); |
| 238 | return tmpstr; | 253 | return tmpstr; |
| 239 | } | 254 | } |
| 240 | 255 | ||
| 241 | static int ul_compare_private( const void *a, const void *b ) { | 256 | static int ul_compare_private(const void *a, const void *b) { |
| 242 | const user *_a = (const user *)a, *_b = (const user *)b; | 257 | const user *_a = (const user *)a, *_b = (const user *)b; |
| 243 | if( _a->last_private > _b->last_private ) return -1; | 258 | if (_a->last_private > _b->last_private) |
| 259 | return -1; | ||
| 244 | return 1; | 260 | return 1; |
| 245 | } | 261 | } |
| 246 | 262 | ||
| 247 | static int ul_compare_begin_of_line_ncase( const void *a, const void *b ) { | 263 | static int ul_compare_begin_of_line_ncase(const void *a, const void *b) { |
| 248 | const user *_a = (const user *)a, *_b = (const user *)b; | 264 | const user *_a = (const user *)a, *_b = (const user *)b; |
| 249 | size_t tmpstr_len; | 265 | size_t tmpstr_len; |
| 250 | int a_i, b_i; | 266 | int a_i, b_i; |
| 251 | 267 | ||
| 252 | /* First ensure that users in current channel win */ | 268 | /* First ensure that users in current channel win */ |
| 253 | if( !(_a->flags & UL_IN_MY_CHAN ) ) return 1; | 269 | if (!(_a->flags & UL_IN_MY_CHAN)) |
| 254 | if( !(_b->flags & UL_IN_MY_CHAN ) ) return -1; | 270 | return 1; |
| 255 | 271 | if (!(_b->flags & UL_IN_MY_CHAN)) | |
| 256 | tmpstr_len = strlen( tmpstr ); | 272 | return -1; |
| 257 | a_i = strncasecmp( _a->nick, tmpstr, tmpstr_len ); | 273 | |
| 258 | b_i = strncasecmp( _b->nick, tmpstr, tmpstr_len ); | 274 | tmpstr_len = strlen(tmpstr); |
| 259 | 275 | a_i = strncasecmp(_a->nick, tmpstr, tmpstr_len); | |
| 260 | if( a_i && b_i ) return 0; // Both nicks dont match | 276 | b_i = strncasecmp(_b->nick, tmpstr, tmpstr_len); |
| 261 | if( !a_i && b_i ) return -1; // a matches insensitive, b doesnt | 277 | |
| 262 | if( a_i && !b_i ) return 1; // b matches insensitive, a doesnt | 278 | if (a_i && b_i) |
| 279 | return 0; // Both nicks dont match | ||
| 280 | if (!a_i && b_i) | ||
| 281 | return -1; // a matches insensitive, b doesnt | ||
| 282 | if (a_i && !b_i) | ||
| 283 | return 1; // b matches insensitive, a doesnt | ||
| 263 | 284 | ||
| 264 | /* From here both nicks match the prefix, ensure that own_nick | 285 | /* From here both nicks match the prefix, ensure that own_nick |
| 265 | always appears last */ | 286 | always appears last */ |
| 266 | if( _a->flags & UL_ME ) return 1; | 287 | if (_a->flags & UL_ME) |
| 267 | if( _b->flags & UL_ME ) return -1; | 288 | return 1; |
| 289 | if (_b->flags & UL_ME) | ||
| 290 | return -1; | ||
| 268 | 291 | ||
| 269 | /* Now the user with the most recent public activity wins */ | 292 | /* Now the user with the most recent public activity wins */ |
| 270 | if( _a->last_public > _b->last_public ) return -1; | 293 | if (_a->last_public > _b->last_public) |
| 294 | return -1; | ||
| 271 | 295 | ||
| 272 | return 1; | 296 | return 1; |
| 273 | } | 297 | } |
| 274 | 298 | ||
| 275 | static int ul_compare_begin_of_line_case( const void *a, const void *b ) { | 299 | static int ul_compare_begin_of_line_case(const void *a, const void *b) { |
| 276 | const user *_a = (const user *)a, *_b = (const user *)b; | 300 | const user *_a = (const user *)a, *_b = (const user *)b; |
| 277 | size_t tmpstr_len; | 301 | size_t tmpstr_len; |
| 278 | int a_i, b_i, a_s, b_s; | 302 | int a_i, b_i, a_s, b_s; |
| 279 | 303 | ||
| 280 | /* First ensure that users in current channel win */ | 304 | /* First ensure that users in current channel win */ |
| 281 | if( !(_a->flags & UL_IN_MY_CHAN ) ) return 1; | 305 | if (!(_a->flags & UL_IN_MY_CHAN)) |
| 282 | if( !(_b->flags & UL_IN_MY_CHAN ) ) return -1; | 306 | return 1; |
| 283 | 307 | if (!(_b->flags & UL_IN_MY_CHAN)) | |
| 284 | tmpstr_len = strlen( tmpstr ); | 308 | return -1; |
| 285 | a_i = strncasecmp( _a->nick, tmpstr, tmpstr_len ); | 309 | |
| 286 | a_s = strncmp ( _a->nick, tmpstr, tmpstr_len ); | 310 | tmpstr_len = strlen(tmpstr); |
| 287 | b_i = strncasecmp( _b->nick, tmpstr, tmpstr_len ); | 311 | a_i = strncasecmp(_a->nick, tmpstr, tmpstr_len); |
| 288 | b_s = strncmp ( _b->nick, tmpstr, tmpstr_len ); | 312 | a_s = strncmp(_a->nick, tmpstr, tmpstr_len); |
| 289 | 313 | b_i = strncasecmp(_b->nick, tmpstr, tmpstr_len); | |
| 290 | if( a_i && b_i ) return 0; // Both nicks dont match at all | 314 | b_s = strncmp(_b->nick, tmpstr, tmpstr_len); |
| 291 | if( !a_i && b_i ) return -1; // a matches insensitive, b doesnt | 315 | |
| 292 | if( a_i && !b_i ) return 1; // b matches insensitive, a doesnt | 316 | if (a_i && b_i) |
| 293 | 317 | return 0; // Both nicks dont match at all | |
| 294 | if( !a_s && b_s ) return -1; // a matches sensitive, b doesnt | 318 | if (!a_i && b_i) |
| 295 | if( a_s && !b_s ) return 1; // b matches sensitive, a doesnt | 319 | return -1; // a matches insensitive, b doesnt |
| 320 | if (a_i && !b_i) | ||
| 321 | return 1; // b matches insensitive, a doesnt | ||
| 322 | |||
| 323 | if (!a_s && b_s) | ||
| 324 | return -1; // a matches sensitive, b doesnt | ||
| 325 | if (a_s && !b_s) | ||
| 326 | return 1; // b matches sensitive, a doesnt | ||
| 296 | 327 | ||
| 297 | /* From now we know that both match with same quality, ensure | 328 | /* From now we know that both match with same quality, ensure |
| 298 | that own nick always appears last */ | 329 | that own nick always appears last */ |
| 299 | if( _a->flags & UL_ME ) return 1; | 330 | if (_a->flags & UL_ME) |
| 300 | if( _b->flags & UL_ME ) return -1; | 331 | return 1; |
| 332 | if (_b->flags & UL_ME) | ||
| 333 | return -1; | ||
| 301 | 334 | ||
| 302 | /* Now the user with the most recent public activity wins */ | 335 | /* Now the user with the most recent public activity wins */ |
| 303 | if( _a->last_public > _b->last_public ) return -1; | 336 | if (_a->last_public > _b->last_public) |
| 337 | return -1; | ||
| 304 | 338 | ||
| 305 | return 1; | 339 | return 1; |
| 306 | } | 340 | } |
| 307 | 341 | ||
| 308 | static int ul_compare_middle_ncase( const void *a, const void *b ) { | 342 | static int ul_compare_middle_ncase(const void *a, const void *b) { |
| 309 | const user *_a = (const user *)a, *_b = (const user *)b; | 343 | const user *_a = (const user *)a, *_b = (const user *)b; |
| 310 | 344 | ||
| 311 | /* Ensure that own nick appears last in list */ | 345 | /* Ensure that own nick appears last in list */ |
| 312 | if( _a->flags & UL_ME ) return 1; | 346 | if (_a->flags & UL_ME) |
| 313 | if( _b->flags & UL_ME ) return -1; | 347 | return 1; |
| 348 | if (_b->flags & UL_ME) | ||
| 349 | return -1; | ||
| 314 | 350 | ||
| 315 | return strcasecmp( _a->nick, _b->nick ); | 351 | return strcasecmp(_a->nick, _b->nick); |
| 316 | } | 352 | } |
| 317 | 353 | ||
| 318 | static int ul_compare_middle_case( const void *a, const void *b ) { | 354 | static int ul_compare_middle_case(const void *a, const void *b) { |
| 319 | const user *_a = (const user *)a, *_b = (const user *)b; | 355 | const user *_a = (const user *)a, *_b = (const user *)b; |
| 320 | size_t tmpstr_len; | 356 | size_t tmpstr_len; |
| 321 | int a_s, b_s; | 357 | int a_s, b_s; |
| 322 | 358 | ||
| 323 | /* Ensure that own nick appears last in list */ | 359 | /* Ensure that own nick appears last in list */ |
| 324 | if( _a->flags & UL_ME ) return 1; | 360 | if (_a->flags & UL_ME) |
| 325 | if( _b->flags & UL_ME ) return -1; | 361 | return 1; |
| 362 | if (_b->flags & UL_ME) | ||
| 363 | return -1; | ||
| 326 | 364 | ||
| 327 | tmpstr_len = strlen( tmpstr ); | 365 | tmpstr_len = strlen(tmpstr); |
| 328 | a_s = strncmp( _a->nick, tmpstr, tmpstr_len ); | 366 | a_s = strncmp(_a->nick, tmpstr, tmpstr_len); |
| 329 | b_s = strncmp( _b->nick, tmpstr, tmpstr_len ); | 367 | b_s = strncmp(_b->nick, tmpstr, tmpstr_len); |
| 330 | 368 | ||
| 331 | if( !a_s && b_s ) return -1; // a matches sensitive, b doesnt | 369 | if (!a_s && b_s) |
| 332 | if( a_s && !b_s ) return 1; // b matches sensitive, a doesnt | 370 | return -1; // a matches sensitive, b doesnt |
| 371 | if (a_s && !b_s) | ||
| 372 | return 1; // b matches sensitive, a doesnt | ||
| 333 | 373 | ||
| 334 | /* From now both strings either both or both dont match | 374 | /* From now both strings either both or both dont match |
| 335 | decide their position by case insensitive match */ | 375 | decide their position by case insensitive match */ |
| 336 | return strcasecmp( _a->nick, _b->nick ); | 376 | return strcasecmp(_a->nick, _b->nick); |
| 337 | } | 377 | } |
| 338 | 378 | ||
| 339 | /* Nick completion function for readline */ | 379 | /* Nick completion function for readline */ |
| 340 | char **ul_complete_user(char *text, int start, int end ) { | 380 | char **ul_complete_user(char *text, int start, int end) { |
| 341 | char **result = 0; | 381 | char **result = 0; |
| 342 | int i, result_count = 0, dict_result_count = 0; | 382 | int i, result_count = 0, dict_result_count = 0; |
| 343 | 383 | ||
| 344 | /* Never want readline to complete filenames */ | 384 | /* Never want readline to complete filenames */ |
| 345 | rl_attempted_completion_over = 1; | 385 | rl_attempted_completion_over = 1; |
| 346 | 386 | ||
| 347 | /* Check for amount of custom dict matches */ | 387 | /* Check for amount of custom dict matches */ |
| 348 | if( end && ( start != end ) ) | 388 | if (end && (start != end)) |
| 349 | for( i=0; i<g_dict_len; ++i ) | 389 | for (i = 0; i < g_dict_len; ++i) |
| 350 | if( !strncasecmp( g_dict[i], text+start, end-start ) ) | 390 | if (!strncasecmp(g_dict[i], text + start, end - start)) |
| 351 | ++dict_result_count; | 391 | ++dict_result_count; |
| 352 | 392 | ||
| 353 | /* Prepare return array ... of max g_users_count (char*) | 393 | /* Prepare return array ... of max g_users_count (char*) |
| 354 | Plus least common prefix in [0] and null terminator | 394 | Plus least common prefix in [0] and null terminator |
| 355 | */ | 395 | */ |
| 356 | result = malloc( sizeof(char*) * ( 2 + g_users_count + dict_result_count ) ); | 396 | result = malloc(sizeof(char *) * (2 + g_users_count + dict_result_count)); |
| 357 | if( !result ) return 0; | 397 | if (!result) |
| 398 | return 0; | ||
| 358 | 399 | ||
| 359 | if( start == 0 && end == 0 ) { | 400 | if (start == 0 && end == 0) { |
| 360 | /* Completion on begin of line yields list of everyone we | 401 | /* Completion on begin of line yields list of everyone we |
| 361 | were in private conversation, sorted by time of last .m */ | 402 | were in private conversation, sorted by time of last .m */ |
| 362 | qsort( g_users, g_users_count, sizeof(user), ul_compare_private ); | 403 | qsort(g_users, g_users_count, sizeof(user), ul_compare_private); |
| 363 | for( i=0; i<g_users_count; ++i ) | 404 | for (i = 0; i < g_users_count; ++i) |
| 364 | if( g_users[i].last_private ) { | 405 | if (g_users[i].last_private) { |
| 365 | snprintf( tmpstr, TMPSTRSIZE, ".m %s", g_users[i].nick ); | 406 | snprintf(tmpstr, TMPSTRSIZE, ".m %s", g_users[i].nick); |
| 366 | result[++result_count] = strdup(tmpstr); | 407 | result[++result_count] = strdup(tmpstr); |
| 367 | } | 408 | } |
| 368 | /* No common prefix */ | 409 | /* No common prefix */ |
| 369 | if( result_count ) result[0] = strdup(""); | 410 | if (result_count) |
| 411 | result[0] = strdup(""); | ||
| 370 | 412 | ||
| 371 | } else if( start == 0 && end > 0 ) { | 413 | } else if (start == 0 && end > 0) { |
| 372 | /* Completion on begin of line with some chars already typed yields | 414 | /* Completion on begin of line with some chars already typed yields |
| 373 | a list of everyone in channel, matching prefix, sorted by last | 415 | a list of everyone in channel, matching prefix, sorted by last |
| 374 | public activity */ | 416 | public activity */ |
| 375 | snprintf( tmpstr, end + 1, "%s", text ); | 417 | snprintf(tmpstr, end + 1, "%s", text); |
| 376 | if( ul_case_first ) | 418 | if (ul_case_first) |
| 377 | qsort( g_users, g_users_count, sizeof(user), ul_compare_begin_of_line_case ); | 419 | qsort(g_users, g_users_count, sizeof(user), |
| 420 | ul_compare_begin_of_line_case); | ||
| 378 | else | 421 | else |
| 379 | qsort( g_users, g_users_count, sizeof(user), ul_compare_begin_of_line_ncase ); | 422 | qsort(g_users, g_users_count, sizeof(user), |
| 423 | ul_compare_begin_of_line_ncase); | ||
| 380 | 424 | ||
| 381 | for( i=0; i<g_users_count; ++i ) | 425 | for (i = 0; i < g_users_count; ++i) |
| 382 | if( ( g_users[i].flags & UL_IN_MY_CHAN ) && !strncasecmp( g_users[i].nick, tmpstr, end ) ) { | 426 | if ((g_users[i].flags & UL_IN_MY_CHAN) && |
| 383 | snprintf( tmpstr, TMPSTRSIZE, "%s:", g_users[i].nick ); | 427 | !strncasecmp(g_users[i].nick, tmpstr, end)) { |
| 428 | snprintf(tmpstr, TMPSTRSIZE, "%s:", g_users[i].nick); | ||
| 384 | result[++result_count] = strdup(tmpstr); | 429 | result[++result_count] = strdup(tmpstr); |
| 385 | } | 430 | } |
| 386 | 431 | ||
| 387 | /* Copy matches from personal dict to the end */ | 432 | /* Copy matches from personal dict to the end */ |
| 388 | for( i=0; i<g_dict_len; ++i ) | 433 | for (i = 0; i < g_dict_len; ++i) |
| 389 | if( !strncasecmp( g_dict[i], tmpstr, end-start ) ) { | 434 | if (!strncasecmp(g_dict[i], tmpstr, end - start)) { |
| 390 | snprintf( tmpstr, TMPSTRSIZE, "%s:", g_dict[i] ); | 435 | snprintf(tmpstr, TMPSTRSIZE, "%s:", g_dict[i]); |
| 391 | result[++result_count] = strdup(tmpstr); | 436 | result[++result_count] = strdup(tmpstr); |
| 392 | } | 437 | } |
| 393 | 438 | ||
| 394 | /* Copy common prefix */ | 439 | /* Copy common prefix */ |
| 395 | if( result_count ) result[0] = strndup(text, end); | 440 | if (result_count) |
| 396 | } else if( start != end ) { | 441 | result[0] = strndup(text, end); |
| 442 | } else if (start != end) { | ||
| 397 | /* Completion in the middle of the line most likely is a .m XY<TAB> | 443 | /* Completion in the middle of the line most likely is a .m XY<TAB> |
| 398 | and thus should complete all users, sorted alphabetically without | 444 | and thus should complete all users, sorted alphabetically without |
| 399 | preferences. */ | 445 | preferences. */ |
| 400 | snprintf( tmpstr, end - start + 1, "%s", text ); | 446 | snprintf(tmpstr, end - start + 1, "%s", text); |
| 401 | if( ul_case_first ) | 447 | if (ul_case_first) |
| 402 | qsort( g_users, g_users_count, sizeof(user), ul_compare_middle_case ); | 448 | qsort(g_users, g_users_count, sizeof(user), ul_compare_middle_case); |
| 403 | else | 449 | else |
| 404 | qsort( g_users, g_users_count, sizeof(user), ul_compare_middle_ncase ); | 450 | qsort(g_users, g_users_count, sizeof(user), ul_compare_middle_ncase); |
| 405 | 451 | ||
| 406 | for( i=0; i<g_users_count; ++i ) | 452 | for (i = 0; i < g_users_count; ++i) |
| 407 | if( !strncasecmp( g_users[i].nick, tmpstr, end - start ) ) | 453 | if (!strncasecmp(g_users[i].nick, tmpstr, end - start)) |
| 408 | result[++result_count] = strdup(g_users[i].nick); | 454 | result[++result_count] = strdup(g_users[i].nick); |
| 409 | 455 | ||
| 410 | /* Copy matches from personal dict to the end */ | 456 | /* Copy matches from personal dict to the end */ |
| 411 | for( i=0; i<g_dict_len; ++i ) | 457 | for (i = 0; i < g_dict_len; ++i) |
| 412 | if( !strncasecmp( g_dict[i], tmpstr, end-start ) ) | 458 | if (!strncasecmp(g_dict[i], tmpstr, end - start)) |
| 413 | result[++result_count] = strdup(g_dict[i]); | 459 | result[++result_count] = strdup(g_dict[i]); |
| 414 | 460 | ||
| 415 | /* Copy common prefix */ | 461 | /* Copy common prefix */ |
| 416 | if( result_count ) result[0] = strndup(text, end - start); | 462 | if (result_count) |
| 463 | result[0] = strndup(text, end - start); | ||
| 417 | } /* else: completion of an empty word in the middle yields nothing */ | 464 | } /* else: completion of an empty word in the middle yields nothing */ |
| 418 | 465 | ||
| 419 | if( !result_count ) { | 466 | if (!result_count) { |
| 420 | free( result ); | 467 | free(result); |
| 421 | result = 0; | 468 | result = 0; |
| 422 | } else | 469 | } else |
| 423 | result[++result_count] = 0; | 470 | result[++result_count] = 0; |
| @@ -10,7 +10,7 @@ | |||
| 10 | * without even the implied warranty of merchantability or fitness for a | 10 | * without even the implied warranty of merchantability or fitness for a |
| 11 | * particular purpose. In no event shall the copyright holder be liable for | 11 | * particular purpose. In no event shall the copyright holder be liable for |
| 12 | * any direct, indirect, incidental or special damages arising in any way out | 12 | * any direct, indirect, incidental or special damages arising in any way out |
| 13 | * of the use of this software. | 13 | * of the use of this software. |
| 14 | * | 14 | * |
| 15 | */ | 15 | */ |
| 16 | 16 | ||
| @@ -18,56 +18,127 @@ | |||
| 18 | typedef enum { SM_IGNORE, SM_INFO, SM_USERINFO, SM_CHANNEL, SM_ERROR } smtype; | 18 | typedef enum { SM_IGNORE, SM_INFO, SM_USERINFO, SM_CHANNEL, SM_ERROR } smtype; |
| 19 | 19 | ||
| 20 | /* servermessage structure */ | 20 | /* servermessage structure */ |
| 21 | struct servermessage | 21 | struct servermessage { |
| 22 | { | 22 | char id[4]; /* three-character message id */ |
| 23 | char id[4]; /* three-character message id */ | 23 | smtype type; /* message type */ |
| 24 | smtype type; /* message type */ | 24 | void (*funct)(char *); /* function used by client */ |
| 25 | void (*funct) (char *); /* function used by client */ | 25 | void (*hook)(char *); /* function hook for scripting */ |
| 26 | void (*hook) (char *); /* function hook for scripting */ | ||
| 27 | }; | 26 | }; |
| 28 | typedef struct servermessage servermessage; | 27 | typedef struct servermessage servermessage; |
| 29 | 28 | ||
| 30 | /* configuration types and variable numbers */ | 29 | /* configuration types and variable numbers */ |
| 31 | typedef enum { CO_NIL, CO_STR, CO_INT } conftype; | 30 | typedef enum { CO_NIL, CO_STR, CO_INT } conftype; |
| 32 | typedef enum { CF_NIL, CF_NICK, CF_FROM, CF_SERVERHOST, CF_SERVERPORT, | 31 | typedef enum { |
| 33 | CF_CIPHERSUITE, CF_CONFIGFILE, CF_CERTFILE, CF_KEYFILE, CF_FORMFILE, | 32 | CF_NIL, |
| 34 | CF_LOGINSCRIPT, CF_FINGERPRINT, CF_PINFINGER, CF_USESSL, CF_IGNSSL, CF_VERIFYSSL, CF_USECERT, | 33 | CF_NICK, |
| 35 | CF_PRIVHEIGHT, CF_PRIVCOLLAPS, CF_HSCROLL, CF_CHANNEL, CF_USETIME, CF_USETOPIC, | 34 | CF_FROM, |
| 36 | CF_SCROLLBPRIV, CF_SCROLLBACK, CF_SCROLLBPRIVT, CF_SCROLLBACKT, CF_ENCODING, | 35 | CF_SERVERHOST, |
| 37 | CF_BELLPRIV, CF_CASEFIRST, CF_AUTORECONN, CF_KEEPALIVE } confopt; | 36 | CF_SERVERPORT, |
| 37 | CF_CIPHERSUITE, | ||
| 38 | CF_CONFIGFILE, | ||
| 39 | CF_CERTFILE, | ||
| 40 | CF_KEYFILE, | ||
| 41 | CF_CAFILE, | ||
| 42 | CF_FORMFILE, | ||
| 43 | CF_LOGINSCRIPT, | ||
| 44 | CF_FINGERPRINT, | ||
| 45 | CF_PINFINGER, | ||
| 46 | CF_USESSL, | ||
| 47 | CF_TLSLIB, | ||
| 48 | CF_IGNSSL, | ||
| 49 | CF_VERIFYSSL, | ||
| 50 | CF_USECERT, | ||
| 51 | CF_PRIVHEIGHT, | ||
| 52 | CF_PRIVCOLLAPS, | ||
| 53 | CF_INVWINBAR, | ||
| 54 | CF_HSCROLL, | ||
| 55 | CF_CHANNEL, | ||
| 56 | CF_USETIME, | ||
| 57 | CF_USETOPIC, | ||
| 58 | CF_SCROLLBPRIV, | ||
| 59 | CF_SCROLLBACK, | ||
| 60 | CF_SCROLLBPRIVT, | ||
| 61 | CF_SCROLLBACKT, | ||
| 62 | CF_ENCODING, | ||
| 63 | CF_BELLPRIV, | ||
| 64 | CF_CASEFIRST, | ||
| 65 | CF_AUTORECONN, | ||
| 66 | CF_KEEPALIVE | ||
| 67 | } confopt; | ||
| 38 | 68 | ||
| 39 | /* format strings */ | 69 | /* format strings */ |
| 40 | typedef enum { FS_PLAIN, FS_CHAN, FS_PRIV, FS_SERV, FS_GLOB, FS_DBG, FS_ERR, | 70 | typedef enum { |
| 41 | FS_IDLE, FS_TIME, FS_CONSOLETIME, FS_TOPICW, FS_NOTOPICW, FS_CONSOLE, FS_CONNECTED, FS_CANTCONNECT, | 71 | FS_PLAIN, |
| 42 | FS_TOPIC, FS_NOTOPIC, FS_CHGTOPIC, FS_USONLINE, FS_USMATCH, FS_SIGNON, FS_SIGNOFF, | 72 | FS_CHAN, |
| 43 | FS_JOIN, FS_LEAVE, FS_NICKCHANGE, FS_UNKNOWNMSG, FS_BOGUSMSG, FS_RXPUBURL, | 73 | FS_PRIV, |
| 44 | FS_MYPUBURL, FS_RXPUBMSG, FS_MYPUBMSG, FS_TXPUBMSG, FS_RXPRIVMSG, FS_TXPRIVMSG, | 74 | FS_SERV, |
| 45 | FS_BGPRIVMSG, FS_PUBTHOUGHT, FS_TXPUBTHOUGHT, FS_TXPUBNTHOUGHT, FS_PUBACTION, | 75 | FS_GLOB, |
| 46 | FS_TXPUBACTION, FS_BGTXPUBACTION, FS_COMMAND, FS_LOCALCOMMAND, FS_BOGUSCOMMAND, | 76 | FS_DBG, |
| 47 | FS_SBINF, FS_MISSTYPED, FS_UNKNCMD, FS_BADREGEX, FS_ERR_STRING } formtstr; | 77 | FS_ERR, |
| 78 | FS_IDLE, | ||
| 79 | FS_TIME, | ||
| 80 | FS_CONSOLETIME, | ||
| 81 | FS_TOPICW, | ||
| 82 | FS_NOTOPICW, | ||
| 83 | FS_CONSOLE, | ||
| 84 | FS_CONNECTED, | ||
| 85 | FS_CANTCONNECT, | ||
| 86 | FS_TOPIC, | ||
| 87 | FS_NOTOPIC, | ||
| 88 | FS_CHGTOPIC, | ||
| 89 | FS_USONLINE, | ||
| 90 | FS_USMATCH, | ||
| 91 | FS_SIGNON, | ||
| 92 | FS_SIGNOFF, | ||
| 93 | FS_JOIN, | ||
| 94 | FS_LEAVE, | ||
| 95 | FS_NICKCHANGE, | ||
| 96 | FS_UNKNOWNMSG, | ||
| 97 | FS_BOGUSMSG, | ||
| 98 | FS_RXPUBURL, | ||
| 99 | FS_MYPUBURL, | ||
| 100 | FS_RXPUBMSG, | ||
| 101 | FS_MYPUBMSG, | ||
| 102 | FS_TXPUBMSG, | ||
| 103 | FS_RXPRIVMSG, | ||
| 104 | FS_TXPRIVMSG, | ||
| 105 | FS_BGPRIVMSG, | ||
| 106 | FS_PUBTHOUGHT, | ||
| 107 | FS_TXPUBTHOUGHT, | ||
| 108 | FS_TXPUBNTHOUGHT, | ||
| 109 | FS_PUBACTION, | ||
| 110 | FS_TXPUBACTION, | ||
| 111 | FS_BGTXPUBACTION, | ||
| 112 | FS_COMMAND, | ||
| 113 | FS_LOCALCOMMAND, | ||
| 114 | FS_BOGUSCOMMAND, | ||
| 115 | FS_SBINF, | ||
| 116 | FS_MISSTYPED, | ||
| 117 | FS_UNKNCMD, | ||
| 118 | FS_BADREGEX, | ||
| 119 | FS_ERR_STRING | ||
| 120 | } formtstr; | ||
| 48 | 121 | ||
| 49 | /* configoption structure */ | 122 | /* configoption structure */ |
| 50 | struct configoption | 123 | struct configoption { |
| 51 | { | 124 | confopt id; |
| 52 | confopt id; | ||
| 53 | conftype type; | 125 | conftype type; |
| 54 | char *varname; | 126 | char *varname; |
| 55 | char *defaultvalue; | 127 | char *defaultvalue; |
| 56 | char *value; | 128 | char *value; |
| 57 | union { | 129 | union { |
| 58 | char **pstr; | 130 | char **pstr; |
| 59 | unsigned int *pint; | 131 | unsigned int *pint; |
| 60 | } localvar; | 132 | } localvar; |
| 61 | }; | 133 | }; |
| 62 | 134 | ||
| 63 | typedef struct configoption configoption; | 135 | typedef struct configoption configoption; |
| 64 | 136 | ||
| 65 | /* format strings */ | 137 | /* format strings */ |
| 66 | struct formatstring | 138 | struct formatstring { |
| 67 | { | ||
| 68 | formtstr id; | 139 | formtstr id; |
| 69 | char *idstring; | 140 | char *idstring; |
| 70 | char *formatstr; | 141 | char *formatstr; |
| 71 | }; | 142 | }; |
| 72 | typedef struct formatstring formatstring; | 143 | typedef struct formatstring formatstring; |
| 73 | 144 | ||
| @@ -82,16 +153,16 @@ extern unsigned int want_tcp_keepalive; | |||
| 82 | #define ERRSTRSIZE 1024 | 153 | #define ERRSTRSIZE 1024 |
| 83 | extern char errstr[]; | 154 | extern char errstr[]; |
| 84 | extern const char *vchat_cl_version; | 155 | extern const char *vchat_cl_version; |
| 85 | void loadcfg (char *file,int complain,void (*lineparser) (char *)); | 156 | void loadcfg(char *file, int complain, void (*lineparser)(char *)); |
| 86 | void loadformats (char *file); | 157 | void loadformats(char *file); |
| 87 | void cleanup(int signal); | 158 | void cleanup(int signal); |
| 88 | 159 | ||
| 89 | /* configuration helper funktions from vchat-client.c */ | 160 | /* configuration helper functions from vchat-client.c */ |
| 90 | char *getformatstr (formtstr id); | 161 | char *getformatstr(formtstr id); |
| 91 | char *getstroption (confopt option); | 162 | char *getstroption(confopt option); |
| 92 | void setstroption (confopt option, char *string); | 163 | void setstroption(confopt option, char *string); |
| 93 | int getintoption (confopt option); | 164 | int getintoption(confopt option); |
| 94 | void setintoption (confopt option, int value); | 165 | void setintoption(confopt option, int value); |
| 95 | 166 | ||
| 96 | /* vchat-ui.c */ | 167 | /* vchat-ui.c */ |
| 97 | extern const char *vchat_ui_version; | 168 | extern const char *vchat_ui_version; |
| @@ -104,76 +175,75 @@ extern char consolestr[]; | |||
| 104 | extern char *encoding; | 175 | extern char *encoding; |
| 105 | 176 | ||
| 106 | /* init / exit functions */ | 177 | /* init / exit functions */ |
| 107 | void initui (void); | 178 | void initui(void); |
| 108 | void exitui (void); | 179 | void exitui(void); |
| 109 | 180 | ||
| 110 | /* called from eventloop in vchat-client.c */ | 181 | /* called from eventloop in vchat-client.c */ |
| 111 | void userinput (void); | 182 | void userinput(void); |
| 112 | 183 | ||
| 113 | /* display various messages */ | 184 | /* display various messages */ |
| 114 | int writechan (char *str); | 185 | int writechan(char *str); |
| 115 | int writepriv (char *str, int maybeep ); | 186 | int writepriv(char *str, int maybeep); |
| 116 | void writeout (const char *str); | 187 | void writeout(const char *str); |
| 117 | void showout (void); | 188 | void showout(void); |
| 118 | void flushout (void); | 189 | void flushout(void); |
| 119 | #define msgout(STR) {flushout();writeout(STR);showout();} | 190 | #define msgout(STR) \ |
| 120 | void hideout (void); | 191 | { \ |
| 121 | int writecf (formtstr id, char *str); | 192 | flushout(); \ |
| 193 | writeout(STR); \ | ||
| 194 | showout(); \ | ||
| 195 | } | ||
| 196 | void hideout(void); | ||
| 197 | int writecf(formtstr id, char *str); | ||
| 198 | /* dumps aggregated connect output in case of a connection error */ | ||
| 199 | void dumpconnect(); | ||
| 200 | void flushconnect(); | ||
| 122 | 201 | ||
| 123 | extern int outputcountdown; | 202 | extern int outputcountdown; |
| 124 | 203 | ||
| 125 | /* update console / topic window */ | 204 | /* update console / topic window */ |
| 126 | void consoleline (char *); | 205 | void consoleline(char *); |
| 127 | void topicline (char *); | 206 | void topicline(char *); |
| 128 | 207 | ||
| 129 | /* prompt for nick or password */ | 208 | /* prompt for nick or password */ |
| 130 | void nickprompt (void); | 209 | void nickprompt(void); |
| 131 | int passprompt (char *buf, int size, int rwflag, void *userdata); | 210 | int passprompt(char *buf, int size, int rwflag, void *userdata); |
| 132 | 211 | ||
| 133 | /* filter functions */ | 212 | /* filter functions */ |
| 134 | void refilterscrollback( void); | 213 | void refilterscrollback(void); |
| 135 | 214 | ||
| 136 | unsigned int addfilter ( char colour, char *regex ); | 215 | unsigned int addfilter(char colour, char *regex); |
| 137 | void removefilter ( char *line ); | 216 | void removefilter(char *line); |
| 138 | void listfilters ( void ); | 217 | void listfilters(void); |
| 139 | void clearfilters ( char colour ); | 218 | void clearfilters(char colour); |
| 140 | 219 | ||
| 141 | void handlequery ( char *line ); | 220 | void handlequery(char *line); |
| 142 | 221 | ||
| 143 | /* vchat-protocol.c */ | 222 | /* vchat-protocol.c */ |
| 144 | extern const char *vchat_io_version; | 223 | extern const char *vchat_io_version; |
| 145 | 224 | void protocol_parsemsg(char *message); | |
| 146 | /* connect/disconnect */ | ||
| 147 | int vcconnect (char *server, char *port); | ||
| 148 | void vcdisconnect (); | ||
| 149 | |||
| 150 | /* network I/O */ | ||
| 151 | void networkinput (void); | ||
| 152 | void networkoutput (char *); | ||
| 153 | 225 | ||
| 154 | /* helpers for vchat-user.c */ | 226 | /* helpers for vchat-user.c */ |
| 155 | void ownjoin (int channel); | 227 | void ownjoin(int channel); |
| 156 | void ownleave (int channel); | 228 | void ownleave(int channel); |
| 157 | void ownnickchange (char *newnick); | 229 | void ownnickchange(const char *newnick); |
| 158 | 230 | ||
| 159 | /* vchat-commands.c */ | 231 | /* vchat-commands.c */ |
| 160 | extern const char *vchat_cm_version; | 232 | extern const char *vchat_cm_version; |
| 161 | void command_version ( char *tail); | 233 | void command_version(char *tail); |
| 162 | 234 | ||
| 163 | /* user input */ | 235 | /* user input */ |
| 164 | void handleline (char *); | 236 | void handleline(char *); |
| 165 | 237 | ||
| 166 | /* struct for defining "/command" handlers */ | 238 | /* struct for defining "/command" handlers */ |
| 167 | typedef struct { | 239 | typedef struct { |
| 168 | int number; | 240 | int number; |
| 169 | char name[10]; | 241 | char name[10]; |
| 170 | int len; | 242 | int len; |
| 171 | void (*handler)(char *); | 243 | void (*handler)(char *); |
| 172 | char *short_help; | 244 | char *short_help; |
| 173 | char *help; | 245 | char *help; |
| 174 | } commandentry; | 246 | } commandentry; |
| 175 | 247 | ||
| 176 | /* vchat-ssl.c */ | 248 | /* vchat-tls.c */ |
| 177 | extern const char *vchat_ssl_version; | 249 | extern const char *vchat_tls_version; |
| 178 | extern const char *vchat_ssl_version_external; | ||
| 179 | void vchat_ssl_get_version_external(); | ||
diff --git a/vchatrc.ex b/vchatrc.ex index c66d1f9..c66d1f9 100755..100644 --- a/vchatrc.ex +++ b/vchatrc.ex | |||
