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