Allow method to pass execgraph arguments containing spaces.
[monky] / src / mboxscan.c
1 /* -*- mode: c; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: t -*-
2  * vim: ts=4 sw=4 noet ai cindent syntax=c
3  *
4  * Conky, a system monitor, based on torsmo
5  *
6  * Any original torsmo code is licensed under the BSD license
7  *
8  * All code written since the fork of torsmo is licensed under the GPL
9  *
10  * Please see COPYING for details
11  *
12  * Copyright (c) 2006 Marco Candrian <mac@calmar.ws>
13  * Copyright (c) 2005-2010 Brenden Matthews, Philip Kovacs, et. al.
14  *      (see AUTHORS)
15  * All rights reserved.
16  *
17  * This program is free software: you can redistribute it and/or modify
18  * it under the terms of the GNU General Public License as published by
19  * the Free Software Foundation, either version 3 of the License, or
20  * (at your option) any later version.
21  *
22  * This program is distributed in the hope that it will be useful,
23  * but WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25  * GNU General Public License for more details.
26  * You should have received a copy of the GNU General Public License
27  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
28  *
29  */
30
31 #include "conky.h"
32 #include "logging.h"
33 #include "mail.h"
34 #include "text_object.h"
35 #include <sys/stat.h>
36 #include <sys/time.h>
37 #include <errno.h>
38
39 #define FROM_WIDTH 10
40 #define SUBJECT_WIDTH 22
41 #define PRINT_MAILS 5
42 #define TIME_DELAY 5
43
44 struct ring_list {
45         char *from;
46         char *subject;
47         struct ring_list *previous;
48         struct ring_list *next;
49 };
50
51 static time_t last_ctime;       /* needed for mutt at least */
52 static time_t last_mtime;       /* not sure what to test: testing both now */
53 static double last_update;
54
55 static int args_ok = 0;
56 static int from_width;
57 static int subject_width;
58 static int print_num_mails;
59 static int time_delay;
60
61 static char mbox_mail_spool[DEFAULT_TEXT_BUFFER_SIZE];
62
63 static void mbox_scan(char *args, char *output, size_t max_len)
64 {
65         int i, u, flag;
66         int force_rescan = 0;
67         char buf[text_buffer_size];
68         struct stat statbuf;
69         struct ring_list *curr = 0, *prev = 0, *startlist = 0;
70         FILE *fp;
71
72         /* output was set to 1 after malloc'ing in conky.c */
73         /* -> beeing able to test it here for catching SIGUSR1 */
74         if (output[0] == 1) {
75                 force_rescan = 1;
76                 output[0] = '\0';
77         }
78
79         if (!args_ok || force_rescan) {
80
81                 char *substr = strstr(args, "-n");
82
83                 if (substr) {
84                         if (sscanf(substr, "-n %i", &print_num_mails) != 1) {
85                                 print_num_mails = PRINT_MAILS;
86                         }
87                 } else {
88                         print_num_mails = PRINT_MAILS;
89                 }
90                 if (print_num_mails < 1) {
91                         print_num_mails = 1;
92                 }
93
94                 substr = strstr(args, "-t");
95                 if (substr) {
96                         if (sscanf(substr, "-t %i", &time_delay) != 1) {
97                                 time_delay = TIME_DELAY;
98                         }
99                 } else {
100                         time_delay = TIME_DELAY;
101                 }
102
103                 substr = strstr(args, "-fw");
104                 if (substr) {
105                         if (sscanf(substr, "-fw %i", &from_width) != 1) {
106                                 from_width = FROM_WIDTH;
107                         }
108                 } else {
109                         from_width = FROM_WIDTH;
110                 }
111
112                 substr = strstr(args, "-sw");
113                 if (substr) {
114                         if (sscanf(substr, "-sw %i", &subject_width) != 1) {
115                                 subject_width = SUBJECT_WIDTH;
116                         }
117                 } else {
118                         subject_width = SUBJECT_WIDTH;
119                 }
120                 /* encapsulated with "'s find first occurrence of " */
121                 if (args[strlen(args) - 1] == '"') {
122                         char *start;
123                         strncpy(mbox_mail_spool, args, DEFAULT_TEXT_BUFFER_SIZE);
124                         start = strchr(mbox_mail_spool, '"') + 1;
125
126                         start[(long) (strrchr(mbox_mail_spool, '"') - start)] = '\0';
127                         strncpy(mbox_mail_spool, start, DEFAULT_TEXT_BUFFER_SIZE);
128                 } else {
129                         char *copy_args = strndup(args, text_buffer_size);
130                         char *tmp = strtok(copy_args, " ");
131                         char *start = tmp;
132
133                         while (tmp) {
134                                 tmp = strtok(NULL, " ");
135                                 if (tmp) {
136                                         start = tmp;
137                                 }
138                         }
139                         strncpy(mbox_mail_spool, start, DEFAULT_TEXT_BUFFER_SIZE);
140                         free(copy_args);
141                 }
142                 if (strlen(mbox_mail_spool) < 1) {
143                         CRIT_ERR(NULL, NULL, "Usage: ${mboxscan [-n <number of messages to print>] "
144                                 "[-fw <from width>] [-sw <subject width>] "
145                                 "[-t <delay in sec> mbox]}");
146                 }
147
148                 /* allowing $MAIL in the config */
149                 if (!strcmp(mbox_mail_spool, "$MAIL")) {
150                         strcpy(mbox_mail_spool, current_mail_spool);
151                 }
152
153                 if (stat(mbox_mail_spool, &statbuf)) {
154                         CRIT_ERR(NULL, NULL, "can't stat %s: %s", mbox_mail_spool, strerror(errno));
155                 }
156                 args_ok = 1;    /* args-computing necessary only once */
157         }
158
159         /* if time_delay not yet reached, then return */
160         if (current_update_time - last_update < time_delay && !force_rescan) {
161                 return;
162         }
163
164         last_update = current_update_time;
165
166         /* mbox still exists? and get stat-infos */
167         if (stat(mbox_mail_spool, &statbuf)) {
168                 NORM_ERR("can't stat %s: %s", mbox_mail_spool, strerror(errno));
169                 output[0] = '\0';       /* delete any output */
170                 return;
171         }
172
173         /* modification time has not changed, so skip scanning the box */
174         if (statbuf.st_ctime == last_ctime && statbuf.st_mtime == last_mtime
175                         && !force_rescan) {
176                 return;
177         }
178
179         last_ctime = statbuf.st_ctime;
180         last_mtime = statbuf.st_mtime;
181
182         /* build up double-linked ring-list to hold data, while scanning down the
183          * mbox */
184         for (i = 0; i < print_num_mails; i++) {
185                 curr = (struct ring_list *) malloc(sizeof(struct ring_list));
186                 curr->from = (char *) malloc(sizeof(char[from_width + 1]));
187                 curr->subject = (char *) malloc(sizeof(char[subject_width + 1]));
188                 curr->from[0] = '\0';
189                 curr->subject[0] = '\0';
190
191                 if (i == 0) {
192                         startlist = curr;
193                 }
194                 if (i > 0) {
195                         curr->previous = prev;
196                         prev->next = curr;
197                 }
198                 prev = curr;
199         }
200
201         /* connect end to start for an endless loop-ring */
202         startlist->previous = curr;
203         curr->next = startlist;
204
205         /* mbox */
206         fp = fopen(mbox_mail_spool, "r");
207         if (!fp) {
208                 return;
209         }
210
211         /* first find a "From " to set it to 0 for header-sarchings */
212         flag = 1;
213         while (!feof(fp)) {
214                 if (fgets(buf, text_buffer_size, fp) == NULL) {
215                         break;
216                 }
217
218                 if (strncmp(buf, "From ", 5) == 0) {
219                         curr = curr->next;
220
221                         /* skip until \n */
222                         while (strchr(buf, '\n') == NULL && !feof(fp)) {
223                                 fgets(buf, text_buffer_size, fp);
224                         }
225
226                         flag = 0;       /* in the headers now */
227                         continue;
228                 }
229
230                 if (flag == 1) {        /* in the body, so skip */
231                         continue;
232                 }
233
234                 if (buf[0] == '\n') {
235                         /* beyond the headers now (empty line), skip until \n */
236                         /* then search for new mail ("From ") */
237
238                         while (strchr(buf, '\n') == NULL && !feof(fp)) {
239                                 fgets(buf, text_buffer_size, fp);
240                         }
241                         flag = 1;       /* in the body now */
242                         continue;
243                 }
244
245                 if ((strncmp(buf, "X-Status: ", 10) == 0)
246                                 || (strncmp(buf, "Status: R", 9) == 0)) {
247
248                         /* Mail was read or something, so skip that message */
249                         flag = 1;       /* search for next From */
250                         curr->subject[0] = '\0';
251                         curr->from[0] = '\0';
252                         /* (will get current again on new 'From ' finding) */
253                         curr = curr->previous;
254                         /* Skip until \n */
255                         while (strchr(buf, '\n') == NULL && !feof(fp)) {
256                                 fgets(buf, text_buffer_size, fp);
257                         }
258                         continue;
259                 }
260
261                 /* that covers ^From: and ^from: ^From:<tab> */
262                 if (strncmp(buf + 1, "rom:", 4) == 0) {
263
264                         i = 0;
265                         u = 6;  /* no "From: " string needed, so skip */
266                         while (1) {
267
268                                 if (buf[u] == '"') {    /* no quotes around names */
269                                         u++;
270                                         continue;
271                                 }
272
273                                 /* some are: From: <foo@bar.com> */
274                                 if (buf[u] == '<' && i > 1) {
275
276                                         curr->from[i] = '\0';
277                                         /* skip until \n */
278                                         while (strchr(buf, '\n') == NULL && !feof(fp)) {
279                                                 fgets(buf, text_buffer_size, fp);
280                                         }
281                                         break;
282                                 }
283
284                                 if (buf[u] == '\n') {
285                                         curr->from[i] = '\0';
286                                         break;
287                                 }
288
289                                 if (buf[u] == '\0') {
290                                         curr->from[i] = '\0';
291                                         break;
292                                 }
293
294                                 if (i >= from_width) {
295                                         curr->from[i] = '\0';
296                                         /* skip until \n */
297                                         while (strchr(buf, '\n') == NULL && !feof(fp)) {
298                                                 fgets(buf, text_buffer_size, fp);
299                                         }
300                                         break;
301                                 }
302
303                                 /* nothing special so just set it */
304                                 curr->from[i++] = buf[u++];
305                         }
306                 }
307
308                 /* that covers ^Subject: and ^subject: and ^Subjec:<tab> */
309                 if (strncmp(buf + 1, "ubject:", 7) == 0) {
310
311                         i = 0;
312                         u = 9;  /* no "Subject: " string needed, so skip */
313                         while (1) {
314
315                                 if (buf[u] == '\n') {
316                                         curr->subject[i] = '\0';
317                                         break;
318                                 }
319                                 if (buf[u] == '\0') {
320                                         curr->subject[i] = '\0';
321                                         break;
322                                 }
323                                 if (i >= subject_width) {
324                                         curr->subject[i] = '\0';
325
326                                         /* skip until \n */
327                                         while (strchr(buf, '\n') == NULL && !feof(fp)) {
328                                                 fgets(buf, text_buffer_size, fp);
329                                         }
330                                         break;
331                                 }
332
333                                 /* nothing special so just set it */
334                                 curr->subject[i++] = buf[u++];
335                         }
336                 }
337         }
338
339         fclose(fp);
340
341         output[0] = '\0';
342
343         i = print_num_mails;
344         while (i) {
345                 struct ring_list *tmp;
346                 if (curr->from[0] != '\0') {
347                         if (i != print_num_mails) {
348                                 snprintf(buf, text_buffer_size, "\nF: %-*s S: %-*s", from_width,
349                                         curr->from, subject_width, curr->subject);
350                         } else {        /* first time - no \n in front */
351                                 snprintf(buf, text_buffer_size, "F: %-*s S: %-*s", from_width,
352                                         curr->from, subject_width, curr->subject);
353                         }
354                 } else {
355                         snprintf(buf, text_buffer_size, "\n");
356                 }
357                 strncat(output, buf, max_len - strlen(output));
358
359                 tmp = curr;
360                 curr = curr->previous;
361                 free(tmp->from);
362                 free(tmp->subject);
363                 free(tmp);
364
365                 i--;
366         }
367 }
368
369 struct mboxscan_data {
370         char *args;
371         char *output;
372 };
373
374 void parse_mboxscan_arg(struct text_object *obj, const char *arg)
375 {
376         struct mboxscan_data *msd;
377
378         msd = malloc(sizeof(struct mboxscan_data));
379         memset(msd, 0, sizeof(struct mboxscan_data));
380
381         msd->args = strndup(arg, text_buffer_size);
382         msd->output = (char *) malloc(text_buffer_size);
383         /* if '1' (in mboxscan.c) then there was SIGUSR1, hmm */
384         msd->output[0] = 1;
385
386         obj->data.opaque = msd;
387 }
388
389 void print_mboxscan(struct text_object *obj, char *p, int p_max_size)
390 {
391         struct mboxscan_data *msd = obj->data.opaque;
392
393         if (!msd)
394                 return;
395
396         mbox_scan(msd->args, msd->output, text_buffer_size);
397         snprintf(p, p_max_size, "%s", msd->output);
398 }
399
400 void free_mboxscan(struct text_object *obj)
401 {
402         struct mboxscan_data *msd = obj->data.opaque;
403
404         if (!msd)
405                 return;
406         if (msd->args)
407                 free(msd->args);
408         if (msd->output)
409                 free(msd->output);
410         free(obj->data.opaque);
411         obj->data.opaque = NULL;
412 }
413