]> git.meshlink.io Git - meshlink-tiny/blob - examples/chat.c
Use a key/value store with configurable storage callbacks.
[meshlink-tiny] / examples / chat.c
1 #define _POSIX_C_SOURCE 200809L
2
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <strings.h>
7 #include <assert.h>
8 #include <sys/stat.h>
9 #include <sys/types.h>
10 #include <dirent.h>
11 #include "../src/meshlink-tiny.h"
12
13 static void log_message(meshlink_handle_t *mesh, meshlink_log_level_t level, const char *text) {
14         (void)mesh;
15
16         static const char *levelstr[] = {
17                 [MESHLINK_DEBUG] = "\x1b[34mDEBUG",
18                 [MESHLINK_INFO] = "\x1b[32mINFO",
19                 [MESHLINK_WARNING] = "\x1b[33mWARNING",
20                 [MESHLINK_ERROR] = "\x1b[31mERROR",
21                 [MESHLINK_CRITICAL] = "\x1b[31mCRITICAL",
22         };
23
24         fprintf(stderr, "%s:\x1b[0m %s\n", levelstr[level], text);
25 }
26
27 static void receive(meshlink_handle_t *mesh, meshlink_node_t *source, const void *data, size_t len) {
28         (void)mesh;
29
30         const char *msg = data;
31
32         if(!len || msg[len - 1]) {
33                 fprintf(stderr, "Received invalid data from %s\n", source->name);
34                 return;
35         }
36
37         printf("%s says: %s\n", source->name, msg);
38 }
39
40 static void node_status(meshlink_handle_t *mesh, meshlink_node_t *node, bool reachable) {
41         (void)mesh;
42
43         if(reachable) {
44                 printf("%s joined.\n", node->name);
45         } else {
46                 printf("%s left.\n", node->name);
47         }
48 }
49
50 static void parse_command(meshlink_handle_t *mesh, char *buf) {
51         char *arg = strchr(buf, ' ');
52
53         if(arg) {
54                 *arg++ = 0;
55         }
56
57         if(!strcasecmp(buf, "join")) {
58                 if(!arg) {
59                         fprintf(stderr, "/join requires an argument!\n");
60                         return;
61                 }
62
63                 meshlink_stop(mesh);
64
65                 if(!meshlink_join(mesh, arg)) {
66                         fprintf(stderr, "Could not join using invitation: %s\n", meshlink_strerror(meshlink_errno));
67                 } else {
68                         fprintf(stderr, "Invitation accepted!\n");
69                 }
70
71                 if(!meshlink_start(mesh)) {
72                         fprintf(stderr, "Could not restart MeshLink: %s\n", meshlink_strerror(meshlink_errno));
73                         exit(1);
74                 }
75         } else if(!strcasecmp(buf, "quit")) {
76                 printf("Bye!\n");
77                 fclose(stdin);
78         } else if(!strcasecmp(buf, "help")) {
79                 printf(
80                         "<name>: <message>     Send a message to the given node.\n"
81                         "                      Subsequent messages don't need the <name>: prefix.\n"
82                         "/join <invitation>    Join an existing mesh using an invitation.\n"
83                         "/kick <name>          Blacklist the given node.\n"
84                         "/who [<name>]         List all nodes or show information about the given node.\n"
85                         "/quit                 Exit this program.\n"
86                 );
87         } else {
88                 fprintf(stderr, "Unknown command '/%s'\n", buf);
89         }
90 }
91
92 static void parse_input(meshlink_handle_t *mesh, char *buf) {
93         static meshlink_node_t *destination;
94         size_t len;
95
96         if(!buf) {
97                 return;
98         }
99
100         // Remove newline.
101
102         len = strlen(buf);
103
104         if(len && buf[len - 1] == '\n') {
105                 buf[--len] = 0;
106         }
107
108         if(len && buf[len - 1] == '\r') {
109                 buf[--len] = 0;
110         }
111
112         // Ignore empty lines.
113
114         if(!len) {
115                 return;
116         }
117
118         // Commands start with '/'
119
120         if(*buf == '/') {
121                 parse_command(mesh, buf + 1);
122                 return;
123         }
124
125         // Lines in the form "name: message..." set the destination node.
126
127         char *msg = buf;
128         char *colon = strchr(buf, ':');
129
130         if(colon) {
131                 *colon = 0;
132                 msg = colon + 1;
133
134                 if(*msg == ' ') {
135                         msg++;
136                 }
137
138                 destination = meshlink_get_node(mesh, buf);
139
140                 if(!destination) {
141                         fprintf(stderr, "Error looking up '%s': %s\n", buf, meshlink_strerror(meshlink_errno));
142                         return;
143                 }
144         }
145
146         if(!destination) {
147                 fprintf(stderr, "Who are you talking to? Write 'name: message...'\n");
148                 return;
149         }
150
151         if(!meshlink_send(mesh, destination, msg, strlen(msg) + 1)) {
152                 fprintf(stderr, "Could not send message to '%s': %s\n", destination->name, meshlink_strerror(meshlink_errno));
153                 return;
154         }
155
156         printf("Message sent to '%s'.\n", destination->name);
157 }
158
159 static char *flatten(const char *filename) {
160         char *result = strdup(filename);
161         assert(result);
162
163         for(char *c = result; *c; c++) {
164                 if(*c == '/') {
165                         *c = ':';
166                 }
167         }
168
169         return result;
170 }
171
172 static bool load_cb(meshlink_handle_t *mesh, const char *key, void *data, size_t *len) {
173         fprintf(stderr, "load_cb(%s, %s, %p, %zu)\n", mesh->name, key, data, *len);
174         FILE *f = fopen(flatten(key), "r");
175         assert(f);
176         fread(data, 1, *len, f);
177         fseek(f, 0, SEEK_END);
178         *len = ftell(f);
179         assert(!fclose(f));
180         return true;
181 }
182
183 static bool store_cb(meshlink_handle_t *mesh, const char *key, const void *data, size_t len) {
184         fprintf(stderr, "store_cb(%s, %s, %p, %zu)\n", mesh->name, key, data, len);
185         FILE *f = fopen(flatten(key), "w");
186         assert(f);
187         assert(fwrite(data, len, 1, f) == 1);
188         assert(!fclose(f));
189         return true;
190 }
191
192 static bool ls_cb(meshlink_handle_t *mesh, meshlink_ls_entry_cb_t entry_cb) {
193         fprintf(stderr, "ls_cb()");
194         DIR *dir = opendir(".");
195         struct dirent *ent;
196
197         while((ent = readdir(dir))) {
198                 if(ent->d_name[0] != '.') {
199                         entry_cb(mesh, ent->d_name, 0);
200                 }
201         }
202
203         closedir(dir);
204
205         return true;
206 }
207
208 int main(int argc, char *argv[]) {
209         const char *confbase = ".chat";
210         const char *nick = NULL;
211         char buf[1024];
212
213         if(argc > 1) {
214                 confbase = argv[1];
215         }
216
217         if(argc > 2) {
218                 nick = argv[2];
219         }
220
221         assert(mkdir(confbase, 0700) == 0);
222         assert(chdir(confbase) == 0);
223
224         meshlink_set_log_cb(NULL, MESHLINK_DEBUG, log_message);
225         meshlink_open_params_t *params = meshlink_open_params_init(confbase, nick, "chat", DEV_CLASS_STATIONARY);
226         //assert(meshlink_open_params_set_storage_key(params, "12345", 5));
227         assert(meshlink_open_params_set_storage_callbacks(params, load_cb, store_cb, ls_cb));
228         meshlink_handle_t *mesh = meshlink_open_ex(params);
229         meshlink_open_params_free(params);
230
231         if(!mesh) {
232                 fprintf(stderr, "Could not open MeshLink: %s\n", meshlink_strerror(meshlink_errno));
233                 return 1;
234         }
235
236         meshlink_set_receive_cb(mesh, receive);
237         meshlink_set_node_status_cb(mesh, node_status);
238         meshlink_set_log_cb(mesh, MESHLINK_INFO, log_message);
239
240         if(!meshlink_start(mesh)) {
241                 fprintf(stderr, "Could not start MeshLink: %s\n", meshlink_strerror(meshlink_errno));
242                 return 1;
243         }
244
245         printf("Chat started.\nType /help for a list of commands.\n");
246
247         while(fgets(buf, sizeof(buf), stdin)) {
248                 parse_input(mesh, buf);
249         }
250
251         printf("Chat stopping.\n");
252
253         meshlink_stop(mesh);
254         meshlink_close(mesh);
255
256         return 0;
257 }