1 /* This example uses deprecated interfaces: global websocket callbacks.
2    They have been superseeded by URI specific callbacks.
3    See examples/embedded_c for an up to date example.
4    */
5 
6 #include <assert.h>
7 #include <stdlib.h>
8 #include <time.h>
9 #include <string.h>
10 #include "WebSockCallbacks.h"
11 
12 #ifdef _WIN32
13 #include <windows.h>
14 #define mg_sleep(x) Sleep(x)
15 #else
16 #include <unistd.h>
17 #include <pthread.h>
18 #define mg_sleep(x) usleep((x)*1000)
19 #endif
20 
21 
22 static void
send_to_all_websockets(struct mg_context * ctx,const char * data,int data_len)23 send_to_all_websockets(struct mg_context *ctx, const char *data, int data_len)
24 {
25 
26 	int i;
27 	tWebSockContext *ws_ctx = (tWebSockContext *)mg_get_user_data(ctx);
28 
29 	mg_lock_context(ctx);
30 	for (i = 0; i < MAX_NUM_OF_WEBSOCKS; i++) {
31 		if (ws_ctx->socketList[i]
32 		    && (ws_ctx->socketList[i]->webSockState == 2)) {
33 			mg_websocket_write(ws_ctx->socketList[i]->conn,
34 			                   WEBSOCKET_OPCODE_TEXT,
35 			                   data,
36 			                   data_len);
37 		}
38 	}
39 	mg_unlock_context(ctx);
40 }
41 
42 
43 void
websocket_ready_handler(struct mg_connection * conn,void * _ignored)44 websocket_ready_handler(struct mg_connection *conn, void *_ignored)
45 {
46 
47 	int i;
48 	const struct mg_request_info *rq = mg_get_request_info(conn);
49 	struct mg_context *ctx = mg_get_context(conn);
50 	tWebSockContext *ws_ctx = (tWebSockContext *)mg_get_user_data(ctx);
51 	tWebSockInfo *wsock = malloc(sizeof(tWebSockInfo));
52 	assert(wsock);
53 	wsock->webSockState = 0;
54 	mg_set_user_connection_data(conn, wsock);
55 
56 	mg_lock_context(ctx);
57 	for (i = 0; i < MAX_NUM_OF_WEBSOCKS; i++) {
58 		if (0 == ws_ctx->socketList[i]) {
59 			ws_ctx->socketList[i] = wsock;
60 			wsock->conn = conn;
61 			wsock->webSockState = 1;
62 			break;
63 		}
64 	}
65 	printf("\nNew websocket attached: %s:%u\n",
66 	       rq->remote_addr,
67 	       rq->remote_port);
68 	mg_unlock_context(ctx);
69 }
70 
71 
72 static void
websocket_done(tWebSockContext * ws_ctx,tWebSockInfo * wsock)73 websocket_done(tWebSockContext *ws_ctx, tWebSockInfo *wsock)
74 {
75 
76 	int i;
77 
78 	if (wsock) {
79 		wsock->webSockState = 99;
80 		for (i = 0; i < MAX_NUM_OF_WEBSOCKS; i++) {
81 			if (wsock == ws_ctx->socketList[i]) {
82 				ws_ctx->socketList[i] = 0;
83 				break;
84 			}
85 		}
86 		printf("\nClose websocket attached: %s:%u\n",
87 		       mg_get_request_info(wsock->conn)->remote_addr,
88 		       mg_get_request_info(wsock->conn)->remote_port);
89 		free(wsock);
90 	}
91 }
92 
93 
94 int
websocket_data_handler(struct mg_connection * conn,int flags,char * data,size_t data_len,void * _ignored)95 websocket_data_handler(struct mg_connection *conn,
96                        int flags,
97                        char *data,
98                        size_t data_len,
99                        void *_ignored)
100 {
101 
102 	const struct mg_request_info *rq = mg_get_request_info(conn);
103 	tWebSockInfo *wsock = (tWebSockInfo *)rq->conn_data;
104 	struct mg_context *ctx = mg_get_context(conn);
105 	tWebSockContext *ws_ctx = (tWebSockContext *)mg_get_user_data(ctx);
106 	char msg[128];
107 
108 	mg_lock_context(ctx);
109 	if (flags == 136) {
110 		// close websock
111 		websocket_done(ws_ctx, wsock);
112 		mg_set_user_connection_data(conn, NULL);
113 		mg_unlock_context(ctx);
114 		return 1;
115 	}
116 	if (((data_len >= 5) && (data_len < 100) && (flags == 129))
117 	    || (flags == 130)) {
118 
119 		// init command
120 		if ((wsock->webSockState == 1) && (!memcmp(data, "init ", 5))) {
121 			char *chk;
122 			unsigned long gid;
123 			memcpy(msg, data + 5, data_len - 5);
124 			msg[data_len - 5] = 0;
125 			gid = strtoul(msg, &chk, 10);
126 			wsock->initId = gid;
127 			if (gid > 0 && chk != NULL && *chk == 0) {
128 				wsock->webSockState = 2;
129 			}
130 			mg_unlock_context(ctx);
131 			return 1;
132 		}
133 
134 		// chat message
135 		if ((wsock->webSockState == 2) && (!memcmp(data, "msg ", 4))) {
136 			send_to_all_websockets(ctx, data, data_len);
137 			mg_unlock_context(ctx);
138 			return 1;
139 		}
140 	}
141 
142 	// keep alive
143 	if ((data_len == 4) && !memcmp(data, "ping", 4)) {
144 		mg_unlock_context(ctx);
145 		return 1;
146 	}
147 
148 	mg_unlock_context(ctx);
149 	return 0;
150 }
151 
152 
153 void
connection_close_handler(const struct mg_connection * conn,void * _ignored)154 connection_close_handler(const struct mg_connection *conn, void *_ignored)
155 {
156 
157 	const struct mg_request_info *rq = mg_get_request_info(conn);
158 	tWebSockInfo *wsock = (tWebSockInfo *)rq->conn_data;
159 	struct mg_context *ctx = mg_get_context(conn);
160 	tWebSockContext *ws_ctx = (tWebSockContext *)mg_get_user_data(ctx);
161 
162 	mg_lock_context(ctx);
163 	websocket_done(ws_ctx, wsock);
164 	mg_set_user_connection_data(conn, NULL);
165 	mg_unlock_context(ctx);
166 }
167 
168 
169 static void *
eventMain(void * arg)170 eventMain(void *arg)
171 {
172 
173 	char msg[256];
174 	struct mg_context *ctx = (struct mg_context *)arg;
175 	tWebSockContext *ws_ctx = (tWebSockContext *)mg_get_user_data(ctx);
176 
177 	ws_ctx->runLoop = 1;
178 	while (ws_ctx->runLoop) {
179 		time_t t = time(0);
180 		struct tm *timestr = localtime(&t);
181 		strftime(msg, sizeof(msg), "title %c", timestr);
182 		send_to_all_websockets(ctx, msg, strlen(msg));
183 
184 		mg_sleep(1000);
185 	}
186 
187 	return NULL;
188 }
189 
190 
191 void
websock_send_broadcast(struct mg_context * ctx,const char * data,int data_len)192 websock_send_broadcast(struct mg_context *ctx, const char *data, int data_len)
193 {
194 
195 	char buffer[260];
196 
197 	if (data_len <= 256) {
198 		strcpy(buffer, "msg ");
199 		memcpy(buffer + 4, data, data_len);
200 
201 		send_to_all_websockets(ctx, buffer, data_len + 4);
202 	}
203 }
204 
205 
206 void
websock_init_lib(const struct mg_context * ctx)207 websock_init_lib(const struct mg_context *ctx)
208 {
209 
210 	tWebSockContext *ws_ctx = (tWebSockContext *)mg_get_user_data(ctx);
211 	memset(ws_ctx, 0, sizeof(*ws_ctx));
212 	/* todo: use mg_start_thread_id instead of mg_start_thread */
213 	mg_start_thread(eventMain, (void *)ctx);
214 }
215 
216 
217 void
websock_exit_lib(const struct mg_context * ctx)218 websock_exit_lib(const struct mg_context *ctx)
219 {
220 
221 	tWebSockContext *ws_ctx = (tWebSockContext *)mg_get_user_data(ctx);
222 	ws_ctx->runLoop = 0;
223 	/* todo: wait for the thread instead of a timeout */
224 	mg_sleep(2000);
225 }
226