]> Pileus Git - ~andy/lamechat/blob - chat.c
Make logfile optional.
[~andy/lamechat] / chat.c
1 /*
2  * Copyright (C) 2017 Andy Spencer <andy753421@gmail.com>
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 #include <stdarg.h>
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include <time.h>
23
24 #include <errno.h>
25 #include <wordexp.h>
26 #include <sys/stat.h>
27 #include <sys/types.h>
28
29 #include "util.h"
30 #include "conf.h"
31 #include "chat.h"
32 #include "view.h"
33
34 /* Global data */
35 server_t  *servers;
36 channel_t *channels;
37 user_t    *users;
38 message_t *messages;
39 int        history;
40
41 /* Local data */
42 static char *log_dir;
43 static buf_t msg_buf;
44
45 static const char *proto_map[] = {
46         [IRC]  "irc",
47         [XMPP] "xmpp",
48 };
49
50 /* Local functions */
51 void strdcat(char *dst, const char *src, size_t n)
52 {
53         int di = 0, si = 0;
54         while (di < (n-1) && dst[di])
55                 di++;
56         while (di < (n-1) && src[si]) {
57                 if (src[si] == '/')
58                         dst[di++] = (si++,'_');
59                 else
60                         dst[di++] = src[si++];
61         }
62         dst[di] = '\0';
63 }
64
65 static void init_logs()
66 {
67         if (!log_dir)
68                 return;
69
70         wordexp_t wexp;
71         wordexp(log_dir, &wexp, WRDE_NOCMD);
72         if (wexp.we_wordc > 1)
73                 error("Found multiple log dirs: %s\n  %s\n  %s\n  ...",
74                         log_dir, wexp.we_wordv[0], wexp.we_wordv[1]);
75         strset(&log_dir, wexp.we_wordv[0]);
76         if (mkdir(log_dir, 0755) && (errno != EEXIST))
77                 error("Failed to create log dir: %s", log_dir);
78         wordfree(&wexp);
79 }
80
81 static void write_log(message_t *msg)
82 {
83         static char log_path[4096];
84
85         if (!log_dir)
86                 return;
87
88         channel_t *chan = msg->channel;
89         server_t  *srv  = chan->server;
90
91         if (!chan->log) {
92                 /* Create server directory */
93                 strncpy(log_path, log_dir,    sizeof(log_path));
94                 strncat(log_path, "/",        sizeof(log_path));
95                 strdcat(log_path, srv->name,  sizeof(log_path));
96                 if (!mkdir(log_path, 0755)) {
97                         debug("Failed to create server log dir\n");
98                         return;
99                 }
100
101                 /* Open log file directory */
102                 strncat(log_path, "/",        sizeof(log_path));
103                 strdcat(log_path, chan->name, sizeof(log_path));
104                 strncat(log_path, ".log",     sizeof(log_path));
105                 if (!(chan->log = fopen(log_path, "a+"))) {
106                         debug("Cannot open log file: %s\n", log_path);
107                         return;
108                 }
109         }
110
111         time_t timep = msg->when;
112         struct tm *tm = gmtime(&timep);
113
114         fprintf(chan->log, "(%04d-%02d-%02d %02d:%02d:%02d) %s%s %s\n",
115                         tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
116                         tm->tm_hour, tm->tm_min, tm->tm_sec,
117                         msg->from ? msg->from->name : "***",
118                         msg->from ? ":"             : "",
119                         msg->text);
120         fflush(chan->log);
121 }
122
123 /* Chat init */
124 void chat_init(void)
125 {
126         messages = (message_t*)msg_buf.data;
127         history  = 0;
128
129         init_logs();
130         irc_init();
131         xmpp_init();
132 }
133
134 void proto_config(const char *pname, const char *sname, const char *cname,
135                   const char *group, const char *name,
136                   const char *key, const char *value)
137 {
138         protocol_t proto = -1;
139         server_t  *srv   = NULL;
140         channel_t *chan  = NULL;
141
142         for (srv = servers; srv; srv = srv->next)
143                 if (match(sname, srv->name))
144                         break;
145         for (chan = channels; chan; chan = chan->next)
146                 if (match(cname, chan->name))
147                         break;
148
149         if      (pname) proto = get_map(pname, proto_map);
150         else if (srv)   proto = srv->protocol;
151         else if (chan)  proto = chan->server->protocol;
152
153         debug("chat_config: [%s-%s \"%s\"] %s = %s",
154                         proto>=0 ? proto_map[proto] : "none",
155                         group, name, key, value);
156
157         switch (proto) {
158                 case IRC:
159                         irc_config(srv, chan, group, name, key, value);
160                         break;
161                 case XMPP:
162                         xmpp_config(srv, chan, group, name, key, value);
163                         break;
164         }
165 }
166
167 void chat_config(const char *group, const char *name, const char *key, const char *value)
168 {
169         if (match(group, "general")) {
170                 if (match(key, "logdir"))
171                         log_dir = strcopy(get_string(value));
172         }
173         if (match(group, "server")) {
174                 if (match(key, ""))
175                         get_name(name);
176                 else if (match(key, "protocol"))
177                         proto_config(value, NULL, NULL, group, name, key, value);
178                 else
179                         proto_config(NULL, name, NULL, group, name, key, value);
180         }
181         if (match(group, "channel")) {
182                 if (match(key, ""))
183                         get_name(name);
184                 else if (match(key, "server"))
185                         proto_config(NULL, value, NULL, group, name, key, value);
186                 else
187                         proto_config(NULL, NULL, name, group, name, key, value);
188         }
189         if (match(group, "autojoin")) {
190                 if (match(key, ""))
191                         get_name(name);
192                 else
193                         proto_config(NULL, name, key, group, name, key, value);
194         }
195 }
196
197 void chat_recv(channel_t *channel, user_t *from, const char *text)
198 {
199         append(&msg_buf, NULL, sizeof(message_t));
200         messages = (message_t*)msg_buf.data;
201
202         message_t *msg = &messages[history];
203         msg->channel = channel;
204         msg->when = time(NULL);
205         msg->from = from;
206         msg->text = strcopy(text);
207
208         history++;
209         write_log(msg);
210         view_draw();
211 }
212
213 void chat_complete(channel_t *channel, const char *text)
214 {
215         switch (channel->server->protocol) {
216                 case IRC:
217                         irc_complete(channel, text);
218                         break;
219                 case XMPP:
220                         xmpp_complete(channel, text);
221                         break;
222         }
223 }
224
225 void chat_send(channel_t *channel, const char *text)
226 {
227         switch (channel->server->protocol) {
228                 case IRC:
229                         irc_send(channel, text);
230                         break;
231                 case XMPP:
232                         xmpp_send(channel, text);
233                         break;
234         }
235 }
236
237 void chat_update(void)
238 {
239         view_draw();
240 }
241
242 void chat_exit(void)
243 {
244         irc_exit();
245         xmpp_exit();
246
247         for (int i = 0; i < history; i++)
248                 free((void*)messages[i].text);
249         release(&msg_buf);
250 }
251
252 /* Server and channel function */
253 void add_server(server_t *server)
254 {
255         protocol_t proto = server->protocol;
256         debug("adding %s server \"%s\"", proto_map[proto], server->name);
257         server_t **last = &servers;
258         while (*last)
259                 last = &(*last)->next;
260         *last = server;
261 }
262
263 void add_channel(channel_t *channel)
264 {
265         debug("adding %s channel \"%s\"", channel->server->name, channel->name);
266         channel_t **last = &channels;
267         while (*last)
268                 last = &(*last)->next;
269         *last = channel;
270 }
271
272 void add_user(user_t *user)
273 {
274         debug("adding %s user \"%s\"", user->server->name, user->name);
275         user_t **last = &users;
276         while (*last)
277                 last = &(*last)->next;
278         *last = user;
279 }