* Replacing strdup() with strndup() throughout
[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-2008 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         struct ring_list *curr = 0, *prev = 0, *startlist = 0;
68         FILE *fp;
69
70         /* output was set to 1 after malloc'ing in conky.c */
71         /* -> beeing able to test it here for catching SIGUSR1 */
72         if (output[0] == 1) {
73                 force_rescan = 1;
74                 output[0] = '\0';
75         }
76
77         if (!args_ok || force_rescan) {
78
79                 char *substr = strstr(args, "-n");
80
81                 if (substr) {
82                         if (sscanf(substr, "-n %i", &print_mails) != 1) {
83                                 print_mails = PRINT_MAILS;
84                         }
85                 } else {
86                         print_mails = PRINT_MAILS;
87                 }
88                 if (print_mails < 1) {
89                         print_mails = 1;
90                 }
91
92                 substr = strstr(args, "-t");
93                 if (substr) {
94                         if (sscanf(substr, "-t %i", &time_delay) != 1) {
95                                 time_delay = TIME_DELAY;
96                         }
97                 } else {
98                         time_delay = TIME_DELAY;
99                 }
100
101                 substr = strstr(args, "-fw");
102                 if (substr) {
103                         if (sscanf(substr, "-fw %i", &from_width) != 1) {
104                                 from_width = FROM_WIDTH;
105                         }
106                 } else {
107                         from_width = FROM_WIDTH;
108                 }
109
110                 substr = strstr(args, "-sw");
111                 if (substr) {
112                         if (sscanf(substr, "-sw %i", &subject_width) != 1) {
113                                 subject_width = SUBJECT_WIDTH;
114                         }
115                 } else {
116                         subject_width = SUBJECT_WIDTH;
117                 }
118                 /* encapsulated with "'s find first occurrence of " */
119                 if (args[strlen(args) - 1] == '"') {
120                         char *start;
121                         strncpy(mbox_mail_spool, args, DEFAULT_TEXT_BUFFER_SIZE);
122                         start = strchr(mbox_mail_spool, '"') + 1;
123
124                         start[(long) (strrchr(mbox_mail_spool, '"') - start)] = '\0';
125                         strncpy(mbox_mail_spool, start, DEFAULT_TEXT_BUFFER_SIZE);
126                 } else {
127                         char *copy_args = strndup(args, text_buffer_size);
128                         char *tmp = strtok(copy_args, " ");
129                         char *start = tmp;
130
131                         while (tmp) {
132                                 tmp = strtok(NULL, " ");
133                                 if (tmp) {
134                                         start = tmp;
135                                 }
136                         }
137                         strncpy(mbox_mail_spool, start, DEFAULT_TEXT_BUFFER_SIZE);
138                         free(copy_args);
139                 }
140                 if (strlen(mbox_mail_spool) < 1) {
141                         CRIT_ERR("Usage: ${mboxscan [-n <number of messages to print>] "
142                                 "[-fw <from width>] [-sw <subject width>] "
143                                 "[-t <delay in sec> mbox]}");
144                 }
145
146                 /* allowing $MAIL in the config */
147                 if (!strcmp(mbox_mail_spool, "$MAIL")) {
148                         strcpy(mbox_mail_spool, current_mail_spool);
149                 }
150
151                 if (stat(mbox_mail_spool, &statbuf)) {
152                         CRIT_ERR("can't stat %s: %s", mbox_mail_spool, strerror(errno));
153                 }
154                 args_ok = 1;    /* args-computing necessary only once */
155         }
156
157         /* if time_delay not yet reached, then return */
158         if (current_update_time - last_update < time_delay && !force_rescan) {
159                 return;
160         }
161
162         last_update = current_update_time;
163
164         /* mbox still exists? and get stat-infos */
165         if (stat(mbox_mail_spool, &statbuf)) {
166                 ERR("can't stat %s: %s", mbox_mail_spool, strerror(errno));
167                 output[0] = '\0';       /* delete any output */
168                 return;
169         }
170
171         /* modification time has not changed, so skip scanning the box */
172         if (statbuf.st_ctime == last_ctime && statbuf.st_mtime == last_mtime
173                         && !force_rescan) {
174                 return;
175         }
176
177         last_ctime = statbuf.st_ctime;
178         last_mtime = statbuf.st_mtime;
179
180         /* build up double-linked ring-list to hold data, while scanning down the
181          * mbox */
182         for (i = 0; i < print_mails; i++) {
183                 curr = (struct ring_list *) malloc(sizeof(struct ring_list));
184                 curr->from = (char *) malloc(sizeof(char[from_width + 1]));
185                 curr->subject = (char *) malloc(sizeof(char[subject_width + 1]));
186                 curr->from[0] = '\0';
187                 curr->subject[0] = '\0';
188
189                 if (i == 0) {
190                         startlist = curr;
191                 }
192                 if (i > 0) {
193                         curr->previous = prev;
194                         prev->next = curr;
195                 }
196                 prev = curr;
197         }
198
199         /* connect end to start for an endless loop-ring */
200         startlist->previous = curr;
201         curr->next = startlist;
202
203         /* mbox */
204         fp = fopen(mbox_mail_spool, "r");
205         if (!fp) {
206                 return;
207         }
208
209         /* first find a "From " to set it to 0 for header-sarchings */
210         flag = 1;
211         while (!feof(fp)) {
212                 if (fgets(buf, text_buffer_size, fp) == NULL) {
213                         break;
214                 }
215
216                 if (strncmp(buf, "From ", 5) == 0) {
217                         curr = curr->next;
218
219                         /* skip until \n */
220                         while (strchr(buf, '\n') == NULL && !feof(fp)) {
221                                 fgets(buf, text_buffer_size, fp);
222                         }
223
224                         flag = 0;       /* in the headers now */
225                         continue;
226                 }
227
228                 if (flag == 1) {        /* in the body, so skip */
229                         continue;
230                 }
231
232                 if (buf[0] == '\n') {
233                         /* beyond the headers now (empty line), skip until \n */
234                         /* then search for new mail ("From ") */
235
236                         while (strchr(buf, '\n') == NULL && !feof(fp)) {
237                                 fgets(buf, text_buffer_size, fp);
238                         }
239                         flag = 1;       /* in the body now */
240                         continue;
241                 }
242
243                 if ((strncmp(buf, "X-Status: ", 10) == 0)
244                                 || (strncmp(buf, "Status: R", 9) == 0)) {
245
246                         /* Mail was read or something, so skip that message */
247                         flag = 1;       /* search for next From */
248                         curr->subject[0] = '\0';
249                         curr->from[0] = '\0';
250                         /* (will get current again on new 'From ' finding) */
251                         curr = curr->previous;
252                         /* Skip until \n */
253                         while (strchr(buf, '\n') == NULL && !feof(fp)) {
254                                 fgets(buf, text_buffer_size, fp);
255                         }
256                         continue;
257                 }
258
259                 /* that covers ^From: and ^from: ^From:<tab> */
260                 if (strncmp(buf + 1, "rom:", 4) == 0) {
261
262                         i = 0;
263                         u = 6;  /* no "From: " string needed, so skip */
264                         while (1) {
265
266                                 if (buf[u] == '"') {    /* no quotes around names */
267                                         u++;
268                                         continue;
269                                 }
270
271                                 /* some are: From: <foo@bar.com> */
272                                 if (buf[u] == '<' && i > 1) {
273
274                                         curr->from[i] = '\0';
275                                         /* skip until \n */
276                                         while (strchr(buf, '\n') == NULL && !feof(fp)) {
277                                                 fgets(buf, text_buffer_size, fp);
278                                         }
279                                         break;
280                                 }
281
282                                 if (buf[u] == '\n') {
283                                         curr->from[i] = '\0';
284                                         break;
285                                 }
286
287                                 if (buf[u] == '\0') {
288                                         curr->from[i] = '\0';
289                                         break;
290                                 }
291
292                                 if (i >= from_width) {
293                                         curr->from[i] = '\0';
294                                         /* skip until \n */
295                                         while (strchr(buf, '\n') == NULL && !feof(fp)) {
296                                                 fgets(buf, text_buffer_size, fp);
297                                         }
298                                         break;
299                                 }
300
301                                 /* nothing special so just set it */
302                                 curr->from[i++] = buf[u++];
303                         }
304                 }
305
306                 /* that covers ^Subject: and ^subject: and ^Subjec:<tab> */
307                 if (strncmp(buf + 1, "ubject:", 7) == 0) {
308
309                         i = 0;
310                         u = 9;  /* no "Subject: " string needed, so skip */
311                         while (1) {
312
313                                 if (buf[u] == '\n') {
314                                         curr->subject[i] = '\0';
315                                         break;
316                                 }
317                                 if (buf[u] == '\0') {
318                                         curr->subject[i] = '\0';
319                                         break;
320                                 }
321                                 if (i >= subject_width) {
322                                         curr->subject[i] = '\0';
323
324                                         /* skip until \n */
325                                         while (strchr(buf, '\n') == NULL && !feof(fp)) {
326                                                 fgets(buf, text_buffer_size, fp);
327                                         }
328                                         break;
329                                 }
330
331                                 /* nothing special so just set it */
332                                 curr->subject[i++] = buf[u++];
333                         }
334                 }
335         }
336
337         fclose(fp);
338
339         output[0] = '\0';
340
341         i = print_mails;
342         while (i) {
343                 struct ring_list *tmp;
344                 if (curr->from[0] != '\0') {
345                         if (i != print_mails) {
346                                 snprintf(buf, text_buffer_size, "\nF: %-*s S: %-*s", from_width,
347                                         curr->from, subject_width, curr->subject);
348                         } else {        /* first time - no \n in front */
349                                 snprintf(buf, text_buffer_size, "F: %-*s S: %-*s", from_width,
350                                         curr->from, subject_width, curr->subject);
351                         }
352                 } else {
353                         snprintf(buf, text_buffer_size, "\n");
354                 }
355                 strncat(output, buf, max_len - strlen(output));
356
357                 tmp = curr;
358                 curr = curr->previous;
359                 free(tmp->from);
360                 free(tmp->subject);
361                 free(tmp);
362
363                 i--;
364         }
365 }