wmauda: Fix installation dir
[dockapps.git] / wmnotify-1.0.0 / src / wmnotify.c
blobb2c7b272e88c753cb4f180d92136a64ddeb022f1
1 /*
2 * wmnotify.c -- POP3 E-mail notification program
4 * Copyright (C) 2003 Hugo Villeneuve (hugo@hugovil.com)
5 * based on WMPop3 by Scott Holden (scotth@thezone.net)
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
22 /* Define filename_M */
23 #define WMNOTIFY_M 1
25 #if HAVE_CONFIG_H
26 # include "config.h"
27 #endif
29 #include <unistd.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <fcntl.h>
33 #include <errno.h>
34 #include <signal.h>
35 #include <time.h>
36 #include <pthread.h>
37 #include <assert.h>
38 #include <sys/wait.h>
39 #include <sys/types.h>
41 #include "common.h"
42 #include "dockapp.h"
43 #include "pop3.h"
44 #include "imap.h"
45 #include "network.h"
46 #include "xevents.h"
47 #include "options.h"
48 #include "configfile.h"
49 #if defined(HAVE_SNDFILE)
50 # include "sound.h"
51 #endif
52 #include "wmnotify.xpm"
53 #include "wmnotify.h"
56 /* Set in DoubleClick() to stop the new mail animation when the mail client is
57 opened. */
58 static bool animation_stop = false;
60 static int animation_image = MAILBOX_FULL;
62 /* Set in response to signal sent by SingleClick() to force mail check. Also set to true at
63 * startup to force initial check. */
64 static bool manual_check = true;
66 /* Used to signal TimerThread to quit. Inactive for now. */
67 static bool quit = false;
69 static int double_click_notif = false;
71 /* TimerThread ID */
72 static pthread_t timer_thread;
75 inline void
76 ErrorLocation( const char *file, int line )
78 fprintf( stderr, " Error in file \"%s\" at line #%d\n", file, line );
82 void *
83 xmalloc( size_t size, const char *filename, int line_number )
85 void *value;
87 value = malloc( size );
89 if( value == NULL ) {
90 perror( PACKAGE );
91 ErrorLocation( filename, line_number );
92 exit( EXIT_FAILURE );
95 return value;
99 static void
100 DisplayOpenedEmptyMailbox( void )
102 /* Opened and empty mailbox image */
103 copyXPMArea( MAILBOX_OPENED_EMPTY_SRC_X, MAILBOX_OPENED_EMPTY_SRC_Y,
104 MAILBOX_SIZE_X, MAILBOX_SIZE_Y, MAILBOX_DEST_X, MAILBOX_DEST_Y );
105 RedrawWindow();
109 static void
110 DisplayOpenedFullMailbox( void )
112 /* Full mailbox image */
113 copyXPMArea( MAILBOX_OPENED_FULL_SRC_X, MAILBOX_OPENED_FULL_SRC_Y,
114 MAILBOX_SIZE_X, MAILBOX_SIZE_Y,
115 MAILBOX_DEST_X, MAILBOX_DEST_Y );
116 RedrawWindow();
120 static void
121 DisplayClosedMailbox( void )
123 /* Opened mailbox image */
124 copyXPMArea( MAILBOX_CLOSED_SRC_X, MAILBOX_CLOSED_SRC_Y,
125 MAILBOX_SIZE_X, MAILBOX_SIZE_Y,
126 MAILBOX_DEST_X, MAILBOX_DEST_Y );
127 RedrawWindow();
131 static void
132 DisplayExecuteCommandNotification( void )
134 /* Visual notification that the double-click was catched. */
135 copyXPMArea( EXEC_CMD_IMG_SRC_X, EXEC_CMD_IMG_SRC_Y,
136 MAILBOX_SIZE_X, MAILBOX_SIZE_Y, MAILBOX_DEST_X, MAILBOX_DEST_Y );
137 RedrawWindow();
141 static void
142 ExecuteCommand( char *argv[] )
144 pid_t pid;
145 char *msg;
147 /* No command defined, this is not an error. */
148 if( argv[0] == NULL ) {
149 return;
152 pid = fork(); /* fork a child process. */
154 if( pid < 0) {
155 perror( PACKAGE );
156 ErrorLocation( __FILE__, __LINE__ );
157 exit( EXIT_FAILURE );
159 else if( pid == 0 ) { /* Child process */
160 /* When execvp() is successful, it doesn't return; otherwise, it returns
161 -1 and sets errno. */
162 (void) execvp( argv[0], argv );
164 msg = strerror( errno );
165 fprintf( stderr, "%s: The external mail program couldn't be started.\n",
166 PACKAGE);
167 fprintf( stderr, "Check your path or your configuration file for errors.\n"
169 fprintf( stderr, "%s: \"%s\"\n", msg, argv[0] );
170 exit( EXIT_FAILURE );
175 /* single-click --> Checking mail */
176 static void
177 SingleClick( void )
179 int status;
181 if( wmnotify_infos.debug ) {
182 printf( "%s: SingleClick() Entry\n", PACKAGE );
185 /* Sending a signal to awake the TimerThread() thread. */
186 status = pthread_kill( timer_thread, SIGUSR1 );
187 if( status != EXIT_SUCCESS ) {
188 fprintf( stderr, "%s: pthread_kill() error (%d)\n", PACKAGE, status );
189 ErrorLocation( __FILE__, __LINE__ );
190 exit( EXIT_FAILURE );
193 if( wmnotify_infos.debug ) {
194 printf( "%s: SingleClick() Exit\n", PACKAGE );
199 /* Double-click --> Starting external mail client. */
200 static void
201 DoubleClick( void )
203 int status;
205 if( wmnotify_infos.mail_client_argv[0] != NULL ) {
206 /* Starting external mail client. */
207 ExecuteCommand( wmnotify_infos.mail_client_argv );
209 double_click_notif = true;
211 /* Sending a signal to awake the TimerThread() thread. This was previously
212 done with a mutex variable (animation_stop), but this caused a bug when the
213 following sequence was encountered:
214 -The user double-click to start the external mail client
215 -A new E-mail is received shortly after that
216 -The user exit the external mail client
217 -The user manually check for new E-mail
218 -The audio notification sound is played, but no animation image is
219 displayed.
220 This was because setting the mutex variable 'animation_stop' didn't
221 awakened the TimerThread(), but single-clicking awakened it. Since the
222 'animation_stop' variable was still set to true, no animation occured. */
223 status = pthread_kill( timer_thread, SIGUSR2 );
224 if( status != EXIT_SUCCESS ) {
225 fprintf( stderr, "%s: pthread_kill() error (%d)\n", PACKAGE, status );
226 ErrorLocation( __FILE__, __LINE__ );
227 exit( EXIT_FAILURE );
230 DisplayExecuteCommandNotification();
231 sleep(1);
232 DisplayClosedMailbox();
234 double_click_notif = false;
236 else {
237 fprintf( stderr, "%s: Warning: No email-client defined.\n", PACKAGE );
242 static void
243 CatchChildTerminationSignal( int signal )
245 switch( signal ) {
246 case SIGCHLD:
247 /* Wait for Mail Client child process termination. Child enters zombie
248 state: process is dead and most resources are released, but process
249 descriptor remains until parent reaps exit status via wait. */
251 /* The WNOHANG option prevents the call to waitpid from suspending execution
252 of the caller. */
253 (void) waitpid( 0, NULL, WNOHANG );
254 break;
255 default:
256 fprintf( stderr, "%s: Unregistered signal received, exiting.\n", PACKAGE );
257 exit( EXIT_FAILURE );
262 static void
263 CatchTimerSignal( int signal )
265 switch( signal ) {
266 case SIGUSR1:
267 /* Catching the signal sent by the SingleClick() function. */
268 manual_check = true;
269 break;
270 case SIGUSR2:
271 /* Catching the signal sent by the DoubleClick() function. */
272 animation_stop = true;
273 break;
274 default:
275 fprintf( stderr, "%s: CatchTimerSignal(): unknown signal (%d)\n", PACKAGE,
276 signal );
277 ErrorLocation( __FILE__, __LINE__ );
278 exit( EXIT_FAILURE );
283 static void
284 NewMailAnimation( void )
286 if( animation_image == MAILBOX_FULL ) {
287 DisplayOpenedFullMailbox();
288 animation_image = MAILBOX_CLOSED;
289 if( wmnotify_infos.debug ) {
290 printf( "%s: NewMailAnimation() MAILBOX_FULL.\n", PACKAGE );
293 else {
294 DisplayClosedMailbox();
295 animation_image = MAILBOX_FULL;
296 if( wmnotify_infos.debug ) {
297 printf( "%s: NewMailAnimation() MAILBOX_CLOSED.\n", PACKAGE );
303 /* We display the opened mailbox image only when doing a manual check. */
304 static int
305 CheckForNewMail( bool manual_check )
307 int new_messages;
309 if( manual_check == true ) {
310 DisplayOpenedEmptyMailbox();
313 if( wmnotify_infos.protocol == POP3_PROTOCOL ) {
314 new_messages = POP3_CheckForNewMail();
316 else if( wmnotify_infos.protocol == IMAP4_PROTOCOL ) {
317 new_messages = IMAP4_CheckForNewMail();
319 else {
320 ErrorLocation( __FILE__, __LINE__ );
321 exit( EXIT_FAILURE );
324 if( ( manual_check == true ) && ( new_messages > 0 ) ) {
325 animation_image = MAILBOX_FULL;
328 return new_messages;
332 static void *
333 TimerThread( /*@unused@*/ void *arg )
335 int new_messages = 0;
336 int counter = -1;
337 bool animation_running = false;
339 /* For catching the signal SIGUSR1. This signal is sent by the main program thread when the
340 * user is issuing a single-click to manually check for new mails. */
341 (void) signal( SIGUSR1, CatchTimerSignal );
343 /* For catching the signal SIGUSR2. This signal is sent by the main program thread when the
344 * user is issuing a double-click to start ther external mail client. */
345 (void) signal( SIGUSR2, CatchTimerSignal );
347 while( quit == false ) {
348 if( wmnotify_infos.debug ) {
349 printf( "%s: Timer thread iteration.\n", PACKAGE );
351 if( ( manual_check == true ) || ( counter == 0 ) ) {
352 new_messages = CheckForNewMail( manual_check );
353 manual_check = false;
355 if( wmnotify_infos.debug ) {
356 printf( "%s: new messages = %d.\n", PACKAGE, new_messages );
359 if( new_messages > 0 ) {
360 /* Checking if audio notification was already produced. */
361 if( animation_running == false ) {
362 /* Audible notification, if requested in configuration file. */
363 if( wmnotify_infos.audible_notification != false ) {
364 if( strlen( wmnotify_infos.audiofile ) != 0 ) {
365 #if defined(HAVE_SNDFILE)
366 PlayAudioFile( wmnotify_infos.audiofile, wmnotify_infos.volume );
367 #endif
369 else {
370 AudibleBeep();
374 animation_running = true;
376 /* Number of times to execute timer loop before checking again for new mails when the
377 * animation is running (when the animation is running, we sleep for
378 * NEW_MAIL_ANIMATION_DURATION instead of wmnotify_infos.mail_check_interval). We set
379 * the check interval to 30 seconds because we want the new mail condition to be
380 * removed as soon as possible when the new messages are checked. */
381 counter = 30 * 1000000 / NEW_MAIL_ANIMATION_DURATION;
385 if( ( animation_stop == true ) || ( new_messages <= 0 ) ) {
386 if( wmnotify_infos.debug ) {
387 if( animation_stop != false ) {
388 printf( "%s: animation_stop is true\n", PACKAGE );
391 animation_running = false;
392 animation_stop = false;
393 if( double_click_notif == false ) {
394 /* Before exiting, be sure to put NO MAIL image back in place... */
395 DisplayClosedMailbox();
399 /* If sleep() returns because the requested time has elapsed, the value returned will be
400 * 0. If sleep() returns because of premature arousal due to delivery of a signal, the
401 * return value will be the "unslept" amount (the requested time minus the time actually
402 * slept) in seconds. */
404 if( animation_running == false ) {
405 (void) sleep( wmnotify_infos.mail_check_interval );
406 counter = 0;
408 else {
409 NewMailAnimation();
410 (void) usleep( NEW_MAIL_ANIMATION_DURATION );
411 counter--;
414 if( wmnotify_infos.debug ) {
415 printf( "%s: counter = %d\n", PACKAGE, counter );
417 } /* end while */
419 if( wmnotify_infos.debug ) {
420 printf( "%s: Error, TimerThread() exited abnormally\n", PACKAGE );
423 /* This code is never reached for now, because quit is always false. */
424 pthread_exit( NULL );
428 /*******************************************************************************
429 * Main function
430 ******************************************************************************/
432 main( int argc, char *argv[] )
434 int status;
436 /* Initialization */
437 ParseCommandLineOptions( argc, argv );
439 /* Reading configuration options from configuration file. */
440 ConfigurationFileInit();
442 /* For catching the termination signal SIGCHLD when the external mail client
443 program is terminated, thus permitting removing zombi processes... */
444 (void) signal( SIGCHLD, CatchChildTerminationSignal );
446 /* Initialize callback function pointers. */
447 ProcessXlibEventsInit( SingleClick, DoubleClick );
449 /* Initializing and creating a DockApp window. */
450 InitDockAppWindow( argc, argv, wmnotify_xpm, wmnotify_infos.display_arg,
451 wmnotify_infos.geometry_arg );
453 /* Starting thread for periodically checking for new mail. */
454 status = pthread_create( &timer_thread, NULL, TimerThread, NULL );
455 if( status != 0 ) {
456 fprintf( stderr, "%s: Thread creation failed (%d)\n", PACKAGE, status );
457 ErrorLocation( __FILE__, __LINE__ );
458 exit( EXIT_FAILURE );
461 /* Main loop, processing X Events */
462 ProcessXlibEvents();
464 /* This code is never reached for now. */
465 fprintf( stderr, "%s: Program exit\n", PACKAGE );
467 exit( EXIT_SUCCESS );